diff options
531 files changed, 21100 insertions, 12958 deletions
diff --git a/Android.bp b/Android.bp index 78d38c5c2537..53417e8e8042 100644 --- a/Android.bp +++ b/Android.bp @@ -281,17 +281,33 @@ filegroup { filegroup { name: "framework-updatable-sources", srcs: [ + ":framework-mediaprovider-sources", + ":framework-permission-sources", ":framework-sdkextensions-sources", ":framework-statsd-sources", + ":framework-telephony-sources", ":framework-tethering-srcs", - ":updatable-media-srcs", - ":framework-mediaprovider-sources", - ":framework-permission-sources", ":framework-wifi-updatable-sources", - ":framework-telephony-sources", + ":updatable-media-srcs", ] } +java_library { + name: "framework-updatable-stubs-module_libs_api", + static_libs: [ + "framework-media-stubs-module_libs_api", + "framework-mediaprovider-stubs-module_libs_api", + "framework-permission-stubs-module_libs_api", + "framework-sdkextensions-stubs-module_libs_api", + "framework-statsd-stubs-module_libs_api", + "framework-telephony-stubs", // TODO: Update to module_libs_api when there is one. + "framework-tethering-stubs-module_libs_api", + "framework-wifi-stubs-module_libs_api", + ], + sdk_version: "module_current", + visibility: [":__pkg__"], +} + filegroup { name: "framework-all-sources", srcs: [ @@ -307,7 +323,6 @@ java_defaults { name: "framework-aidl-export-defaults", aidl: { export_include_dirs: [ - "apex/media/framework/java", "core/java", "drm/java", "graphics/java", @@ -324,6 +339,12 @@ java_defaults { "rs/java", "sax/java", "telecomm/java", + + // TODO(b/148660295): remove this + "apex/media/framework/java", + + // TODO(b/147699819): remove this + "telephony/java", ], }, } @@ -397,9 +418,7 @@ java_defaults { "app-compat-annotations", "ext", "unsupportedappusage", - "framework-media-stubs-systemapi", - "framework-mediaprovider-stubs-systemapi", - "framework-telephony-stubs", + "framework-updatable-stubs-module_libs_api", ], jarjar_rules: ":framework-jarjar-rules", @@ -465,13 +484,6 @@ java_library { name: "framework-minus-apex", defaults: ["framework-defaults"], srcs: [":framework-non-updatable-sources"], - libs: [ - "framework-sdkextensions-stubs-systemapi", - "framework-statsd-stubs-module_libs_api", - "framework-permission-stubs-systemapi", - "framework-wifi-stubs-systemapi", - "framework-tethering-stubs", - ], installable: true, javac_shard_size: 150, required: [ @@ -512,16 +524,9 @@ java_library { installable: false, // this lib is a build-only library static_libs: [ "framework-minus-apex", - "framework-media-stubs-systemapi", - "framework-mediaprovider-stubs-systemapi", - "framework-permission-stubs-systemapi", - "framework-sdkextensions-stubs-systemapi", - "framework-statsd-stubs-module_libs_api", - "framework-wifi-stubs-systemapi", - "framework-tethering-stubs", - // TODO (b/147688669) should be framework-telephony-stubs + // TODO (b/147688669) should be removed "framework-telephony", - // TODO(jiyong): add stubs for APEXes here + "framework-updatable-stubs-module_libs_api", ], sdk_version: "core_platform", apex_available: ["//apex_available:platform"], @@ -540,9 +545,7 @@ java_library { visibility: [ // DO NOT ADD ANY MORE ENTRIES TO THIS LIST "//external/robolectric-shadows:__subpackages__", - "//frameworks/base/packages/Tethering/common/TetheringLib:__subpackages__", "//frameworks/layoutlib:__subpackages__", - "//frameworks/opt/net/ike:__subpackages__", ], } diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java index c458d1190e51..e042782af366 100644 --- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java +++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java @@ -670,7 +670,7 @@ public class UserLifecycleTests { private void startApp(int userId, String packageName) throws RemoteException { final Context context = InstrumentationRegistry.getContext(); final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null, - context.getPackageName(), context.getFeatureId(), + context.getPackageName(), context.getAttributionTag(), context.getPackageManager().getLaunchIntentForPackage(packageName), null, null, null, 0, 0, null, null, userId); attestTrue("User " + userId + " failed to start " + packageName, diff --git a/apex/Android.bp b/apex/Android.bp index 051986e758d3..151091137c9c 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -43,6 +43,7 @@ stubs_defaults { name: "framework-module-stubs-defaults-publicapi", args: mainline_stubs_args, installable: false, + sdk_version: "current", } stubs_defaults { @@ -50,6 +51,7 @@ stubs_defaults { args: mainline_stubs_args + priv_apps, srcs: [":framework-annotations"], installable: false, + sdk_version: "system_current", } // The defaults for module_libs comes in two parts - defaults for API checks @@ -62,6 +64,7 @@ stubs_defaults { args: mainline_stubs_args + module_libs, srcs: [":framework-annotations"], installable: false, + sdk_version: "module_current", } stubs_defaults { @@ -69,4 +72,5 @@ stubs_defaults { args: mainline_stubs_args + module_libs + priv_apps, srcs: [":framework-annotations"], installable: false, + sdk_version: "module_current", } diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index b9052734b65f..ae8976a2972e 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -53,9 +53,8 @@ import java.util.Objects; * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the * parameters required to schedule work against the calling application. These are constructed * using the {@link JobInfo.Builder}. - * You must specify at least one sort of constraint on the JobInfo object that you are creating. * The goal here is to provide the scheduler with high-level semantics about the work you want to - * accomplish. Doing otherwise with throw an exception in your app. + * accomplish. */ public class JobInfo implements Parcelable { private static String TAG = "JobInfo"; @@ -147,7 +146,7 @@ public class JobInfo implements Parcelable { /** * Query the minimum interval allowed for periodic scheduled jobs. Attempting - * to declare a smaller period that this when scheduling a job will result in a + * to declare a smaller period than this when scheduling a job will result in a * job that is still periodic, but will run with this effective period. * * @return The minimum available interval for scheduling periodic jobs, in milliseconds. diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index b219fd41afec..29c48adb999d 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -139,7 +139,7 @@ import java.util.Map; * @Override * public void onSampleCompleted( * int trackIndex, - * long timeUs, + * long timeMicros, * int flags, * int size, * int offset, @@ -163,7 +163,7 @@ import java.util.Map; * /* destPos= */ 0, * /* size= */ offset); * bytesWrittenCount = bytesWrittenCount - offset; - * publishSample(sampleData, timeUs, flags); + * publishSample(sampleData, timeMicros, flags); * } * * private void ensureSpaceInBuffer(int numberOfBytesToRead) { @@ -187,7 +187,7 @@ public final class MediaParser { */ public static final class SeekMap { - /** Returned by {@link #getDurationUs()} when the duration is unknown. */ + /** Returned by {@link #getDurationMicros()} when the duration is unknown. */ public static final int UNKNOWN_DURATION = Integer.MIN_VALUE; private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap; @@ -205,26 +205,26 @@ public final class MediaParser { * Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the * duration is unknown. */ - public long getDurationUs() { + public long getDurationMicros() { return mExoPlayerSeekMap.getDurationUs(); } /** * Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds. * - * <p>{@code getSeekPoints(timeUs).first} contains the latest seek point for samples with - * timestamp equal to or smaller than {@code timeUs}. + * <p>{@code getSeekPoints(timeMicros).first} contains the latest seek point for samples + * with timestamp equal to or smaller than {@code timeMicros}. * - * <p>{@code getSeekPoints(timeUs).second} contains the earliest seek point for samples with - * timestamp equal to or greater than {@code timeUs}. If a seek point exists for {@code - * timeUs}, the returned pair will contain the same {@link SeekPoint} twice. + * <p>{@code getSeekPoints(timeMicros).second} contains the earliest seek point for samples + * with timestamp equal to or greater than {@code timeMicros}. If a seek point exists for + * {@code timeMicros}, the returned pair will contain the same {@link SeekPoint} twice. * - * @param timeUs A seek time in microseconds. + * @param timeMicros A seek time in microseconds. * @return The corresponding {@link SeekPoint SeekPoints}. */ @NonNull - public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) { - SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs); + public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeMicros) { + SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeMicros); return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second)); } } @@ -254,24 +254,24 @@ public final class MediaParser { @NonNull public static final SeekPoint START = new SeekPoint(0, 0); /** The time of the seek point, in microseconds. */ - public final long timeUs; + public final long timeMicros; /** The byte offset of the seek point. */ public final long position; /** - * @param timeUs The time of the seek point, in microseconds. + * @param timeMicros The time of the seek point, in microseconds. * @param position The byte offset of the seek point. */ - private SeekPoint(long timeUs, long position) { - this.timeUs = timeUs; + private SeekPoint(long timeMicros, long position) { + this.timeMicros = timeMicros; this.position = position; } @Override @NonNull public String toString() { - return "[timeUs=" + timeUs + ", position=" + position + "]"; + return "[timeMicros=" + timeMicros + ", position=" + position + "]"; } @Override @@ -283,12 +283,12 @@ public final class MediaParser { return false; } SeekPoint other = (SeekPoint) obj; - return timeUs == other.timeUs && position == other.position; + return timeMicros == other.timeMicros && position == other.position; } @Override public int hashCode() { - int result = (int) timeUs; + int result = (int) timeMicros; result = 31 * result + (int) position; return result; } @@ -345,25 +345,25 @@ public final class MediaParser { * * @param seekMap The extracted {@link SeekMap}. */ - void onSeekMap(@NonNull SeekMap seekMap); + void onSeekMapFound(@NonNull SeekMap seekMap); /** * Called when the number of tracks is found. * * @param numberOfTracks The number of tracks in the stream. */ - void onTracksFound(int numberOfTracks); + void onTrackCountFound(int numberOfTracks); /** - * Called when new {@link TrackData} is extracted from the stream. + * Called when new {@link TrackData} is found in the stream. * * @param trackIndex The index of the track for which the {@link TrackData} was extracted. * @param trackData The extracted {@link TrackData}. */ - void onTrackData(int trackIndex, @NonNull TrackData trackData); + void onTrackDataFound(int trackIndex, @NonNull TrackData trackData); /** - * Called to write sample data to the output. + * Called when sample data is found in the stream. * * <p>If the invocation of this method returns before the entire {@code inputReader} {@link * InputReader#getLength() length} is consumed, the method will be called again for the @@ -374,15 +374,15 @@ public final class MediaParser { * @param inputReader The {@link InputReader} from which to read the data. * @throws IOException If an exception occurs while reading from {@code inputReader}. */ - void onSampleData(int trackIndex, @NonNull InputReader inputReader) throws IOException; + void onSampleDataFound(int trackIndex, @NonNull InputReader inputReader) throws IOException; /** - * Called once all the data of a sample has been passed to {@link #onSampleData}. + * Called once all the data of a sample has been passed to {@link #onSampleDataFound}. * * <p>Also includes sample metadata, like presentation timestamp and flags. * * @param trackIndex The index of the track to which the sample corresponds. - * @param timeUs The media timestamp associated with the sample, in microseconds. + * @param timeMicros The media timestamp associated with the sample, in microseconds. * @param flags Flags associated with the sample. See {@link MediaCodec * MediaCodec.BUFFER_FLAG_*}. * @param size The size of the sample data, in bytes. @@ -394,7 +394,7 @@ public final class MediaParser { */ void onSampleCompleted( int trackIndex, - long timeUs, + long timeMicros, int flags, int size, int offset, @@ -632,7 +632,7 @@ public final class MediaParser { private Extractor mExtractor; private ExtractorInput mExtractorInput; private long mPendingSeekPosition; - private long mPendingSeekTimeUs; + private long mPendingSeekTimeMicros; // Public methods. @@ -760,7 +760,7 @@ public final class MediaParser { } if (isPendingSeek()) { - mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeUs); + mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeMicros); removePendingSeek(); } @@ -786,7 +786,7 @@ public final class MediaParser { * Seeks within the media container being extracted. * * <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link - * OutputConsumer#onSeekMap(SeekMap)}. + * OutputConsumer#onSeekMapFound(SeekMap)}. * * <p>Following a call to this method, the {@link InputReader} passed to the next invocation of * {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream. @@ -796,9 +796,9 @@ public final class MediaParser { public void seek(@NonNull SeekPoint seekPoint) { if (mExtractor == null) { mPendingSeekPosition = seekPoint.position; - mPendingSeekTimeUs = seekPoint.timeUs; + mPendingSeekTimeMicros = seekPoint.timeMicros; } else { - mExtractor.seek(seekPoint.position, seekPoint.timeUs); + mExtractor.seek(seekPoint.position, seekPoint.timeMicros); } } @@ -836,7 +836,7 @@ public final class MediaParser { private void removePendingSeek() { mPendingSeekPosition = -1; - mPendingSeekTimeUs = -1; + mPendingSeekTimeMicros = -1; } // Private classes. @@ -897,12 +897,12 @@ public final class MediaParser { @Override public void endTracks() { - mOutputConsumer.onTracksFound(mTrackOutputAdapters.size()); + mOutputConsumer.onTrackCountFound(mTrackOutputAdapters.size()); } @Override public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) { - mOutputConsumer.onSeekMap(new SeekMap(exoplayerSeekMap)); + mOutputConsumer.onSeekMapFound(new SeekMap(exoplayerSeekMap)); } } @@ -916,7 +916,7 @@ public final class MediaParser { @Override public void format(Format format) { - mOutputConsumer.onTrackData( + mOutputConsumer.onTrackDataFound( mTrackIndex, new TrackData( toMediaFormat(format), toFrameworkDrmInitData(format.drmInitData))); @@ -927,7 +927,7 @@ public final class MediaParser { throws IOException { mScratchExtractorInputAdapter.setExtractorInput(input, length); long positionBeforeReading = mScratchExtractorInputAdapter.getPosition(); - mOutputConsumer.onSampleData(mTrackIndex, mScratchExtractorInputAdapter); + mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchExtractorInputAdapter); return (int) (mScratchExtractorInputAdapter.getPosition() - positionBeforeReading); } @@ -935,7 +935,7 @@ public final class MediaParser { public void sampleData(ParsableByteArray data, int length) { mScratchParsableByteArrayAdapter.resetWithByteArray(data, length); try { - mOutputConsumer.onSampleData(mTrackIndex, mScratchParsableByteArrayAdapter); + mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchParsableByteArrayAdapter); } catch (IOException e) { // Unexpected. throw new RuntimeException(e); diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java index 0d163cf55e4e..aedba290db1f 100644 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java +++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java @@ -40,7 +40,7 @@ public interface RuntimePermissionsPersistence { * @return the runtime permissions read */ @Nullable - RuntimePermissionsState readAsUser(@NonNull UserHandle user); + RuntimePermissionsState readForUser(@NonNull UserHandle user); /** * Write the runtime permissions to persistence. @@ -50,7 +50,8 @@ public interface RuntimePermissionsPersistence { * @param runtimePermissions the runtime permissions to write * @param user the user to write for */ - void writeAsUser(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user); + void writeForUser(@NonNull RuntimePermissionsState runtimePermissions, + @NonNull UserHandle user); /** * Delete the runtime permissions from persistence. @@ -59,7 +60,7 @@ public interface RuntimePermissionsPersistence { * * @param user the user to delete for */ - void deleteAsUser(@NonNull UserHandle user); + void deleteForUser(@NonNull UserHandle user); /** * Create a new instance of {@link RuntimePermissionsPersistence} implementation. diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java index 0ac0c730998b..e43f59a3377a 100644 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java +++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java @@ -67,7 +67,7 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers @Nullable @Override - public RuntimePermissionsState readAsUser(@NonNull UserHandle user) { + public RuntimePermissionsState readForUser(@NonNull UserHandle user) { File file = getFile(user); try (FileInputStream inputStream = new AtomicFile(file).openRead()) { XmlPullParser parser = Xml.newPullParser(); @@ -172,7 +172,7 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers } @Override - public void writeAsUser(@NonNull RuntimePermissionsState runtimePermissions, + public void writeForUser(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user) { File file = getFile(user); AtomicFile atomicFile = new AtomicFile(file); @@ -252,7 +252,7 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers } @Override - public void deleteAsUser(@NonNull UserHandle user) { + public void deleteForUser(@NonNull UserHandle user) { getFile(user).delete(); } diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java index cd2750a0bee5..c6bfc6d32989 100644 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java +++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java @@ -23,6 +23,7 @@ import android.annotation.SystemApi.Client; import java.util.List; import java.util.Map; +import java.util.Objects; /** * State of all runtime permissions. @@ -61,6 +62,14 @@ public final class RuntimePermissionsState { @NonNull private final Map<String, List<PermissionState>> mSharedUserPermissions; + /** + * Create a new instance of this class. + * + * @param version the version of the runtime permissions + * @param fingerprint the fingerprint of the runtime permissions + * @param packagePermissions the runtime permissions by packages + * @param sharedUserPermissions the runtime permissions by shared users + */ public RuntimePermissionsState(int version, @Nullable String fingerprint, @NonNull Map<String, List<PermissionState>> packagePermissions, @NonNull Map<String, List<PermissionState>> sharedUserPermissions) { @@ -70,32 +79,72 @@ public final class RuntimePermissionsState { mSharedUserPermissions = sharedUserPermissions; } + /** + * Get the version of the runtime permissions. + * + * @return the version of the runtime permissions + */ public int getVersion() { return mVersion; } + /** + * Get the fingerprint of the runtime permissions. + * + * @return the fingerprint of the runtime permissions + */ @Nullable public String getFingerprint() { return mFingerprint; } + /** + * Get the runtime permissions by packages. + * + * @return the runtime permissions by packages + */ @NonNull public Map<String, List<PermissionState>> getPackagePermissions() { return mPackagePermissions; } + /** + * Get the runtime permissions by shared users. + * + * @return the runtime permissions by shared users + */ @NonNull public Map<String, List<PermissionState>> getSharedUserPermissions() { return mSharedUserPermissions; } + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + RuntimePermissionsState that = (RuntimePermissionsState) object; + return mVersion == that.mVersion + && Objects.equals(mFingerprint, that.mFingerprint) + && Objects.equals(mPackagePermissions, that.mPackagePermissions) + && Objects.equals(mSharedUserPermissions, that.mSharedUserPermissions); + } + + @Override + public int hashCode() { + return Objects.hash(mVersion, mFingerprint, mPackagePermissions, mSharedUserPermissions); + } + /** * State of a single permission. */ - public static class PermissionState { + public static final class PermissionState { /** - * Name of the permission. + * The name of the permission. */ @NonNull private final String mName; @@ -106,27 +155,68 @@ public final class RuntimePermissionsState { private final boolean mGranted; /** - * Flags of the permission. + * The flags of the permission. */ private final int mFlags; + /** + * Create a new instance of this class. + * + * @param name the name of the permission + * @param granted whether the permission is granted + * @param flags the flags of the permission + */ public PermissionState(@NonNull String name, boolean granted, int flags) { mName = name; mGranted = granted; mFlags = flags; } + /** + * Get the name of the permission. + * + * @return the name of the permission + */ @NonNull public String getName() { return mName; } + /** + * Get whether the permission is granted. + * + * @return whether the permission is granted + */ public boolean isGranted() { return mGranted; } + /** + * Get the flags of the permission. + * + * @return the flags of the permission + */ public int getFlags() { return mFlags; } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + PermissionState that = (PermissionState) object; + return mGranted == that.mGranted + && mFlags == that.mFlags + && Objects.equals(mName, that.mName); + } + + @Override + public int hashCode() { + return Objects.hash(mName, mGranted, mFlags); + } } } diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java index 64d6545c87cf..2e5a28aa1d6a 100644 --- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java +++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java @@ -40,7 +40,7 @@ public interface RolesPersistence { * @return the roles read */ @Nullable - RolesState readAsUser(@NonNull UserHandle user); + RolesState readForUser(@NonNull UserHandle user); /** * Write the roles to persistence. @@ -50,7 +50,7 @@ public interface RolesPersistence { * @param roles the roles to write * @param user the user to write for */ - void writeAsUser(@NonNull RolesState roles, @NonNull UserHandle user); + void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user); /** * Delete the roles from persistence. @@ -59,7 +59,7 @@ public interface RolesPersistence { * * @param user the user to delete for */ - void deleteAsUser(@NonNull UserHandle user); + void deleteForUser(@NonNull UserHandle user); /** * Create a new instance of {@link RolesPersistence} implementation. diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java index 2346c11e5242..f66257f13ef6 100644 --- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java +++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java @@ -65,7 +65,7 @@ public class RolesPersistenceImpl implements RolesPersistence { @Nullable @Override - public RolesState readAsUser(@NonNull UserHandle user) { + public RolesState readForUser(@NonNull UserHandle user) { File file = getFile(user); try (FileInputStream inputStream = new AtomicFile(file).openRead()) { XmlPullParser parser = Xml.newPullParser(); @@ -146,7 +146,7 @@ public class RolesPersistenceImpl implements RolesPersistence { } @Override - public void writeAsUser(@NonNull RolesState roles, @NonNull UserHandle user) { + public void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user) { File file = getFile(user); AtomicFile atomicFile = new AtomicFile(file); FileOutputStream outputStream = null; @@ -205,7 +205,7 @@ public class RolesPersistenceImpl implements RolesPersistence { } @Override - public void deleteAsUser(@NonNull UserHandle user) { + public void deleteForUser(@NonNull UserHandle user) { getFile(user).delete(); } diff --git a/apex/permission/service/java/com/android/role/persistence/RolesState.java b/apex/permission/service/java/com/android/role/persistence/RolesState.java index 7da9d11f172f..f61efa0e840d 100644 --- a/apex/permission/service/java/com/android/role/persistence/RolesState.java +++ b/apex/permission/service/java/com/android/role/persistence/RolesState.java @@ -22,6 +22,7 @@ import android.annotation.SystemApi; import android.annotation.SystemApi.Client; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -50,6 +51,13 @@ public final class RolesState { @NonNull private final Map<String, Set<String>> mRoles; + /** + * Create a new instance of this class. + * + * @param version the version of the roles + * @param packagesHash the hash of all packages in the system + * @param roles the roles + */ public RolesState(int version, @Nullable String packagesHash, @NonNull Map<String, Set<String>> roles) { mVersion = version; @@ -57,17 +65,51 @@ public final class RolesState { mRoles = roles; } + /** + * Get the version of the roles. + * + * @return the version of the roles + */ public int getVersion() { return mVersion; } + /** + * Get the hash of all packages in the system. + * + * @return the hash of all packages in the system + */ @Nullable public String getPackagesHash() { return mPackagesHash; } + /** + * Get the roles. + * + * @return the roles + */ @NonNull public Map<String, Set<String>> getRoles() { return mRoles; } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + RolesState that = (RolesState) object; + return mVersion == that.mVersion + && Objects.equals(mPackagesHash, that.mPackagesHash) + && Objects.equals(mRoles, that.mRoles); + } + + @Override + public int hashCode() { + return Objects.hash(mVersion, mPackagesHash, mRoles); + } } diff --git a/api/current.txt b/api/current.txt index 2efb30514f75..1e61eefc97e5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -572,6 +572,7 @@ package android { field public static final int elevation = 16843840; // 0x1010440 field public static final int ellipsize = 16842923; // 0x10100ab field public static final int ems = 16843096; // 0x1010158 + field public static final int enableGwpAsan = 16844310; // 0x1010616 field public static final int enableVrMode = 16844069; // 0x1010525 field public static final int enabled = 16842766; // 0x101000e field public static final int end = 16843996; // 0x10104dc @@ -620,7 +621,6 @@ package android { field public static final int fastScrollTextColor = 16843609; // 0x1010359 field public static final int fastScrollThumbDrawable = 16843574; // 0x1010336 field public static final int fastScrollTrackDrawable = 16843577; // 0x1010339 - field public static final int featureId = 16844301; // 0x101060d field public static final int fillAfter = 16843197; // 0x10101bd field public static final int fillAlpha = 16843980; // 0x10104cc field public static final int fillBefore = 16843196; // 0x10101bc @@ -4586,7 +4586,7 @@ package android.app { public final class AsyncNotedAppOp implements android.os.Parcelable { method public int describeContents(); - method @Nullable public String getFeatureId(); + method @Nullable public String getAttributionTag(); method @NonNull public String getMessage(); method @IntRange(from=0) public int getNotingUid(); method @NonNull public String getOp(); @@ -6430,7 +6430,7 @@ package android.app { public final class SyncNotedAppOp implements android.os.Parcelable { ctor public SyncNotedAppOp(@IntRange(from=0L) int, @Nullable String); method public int describeContents(); - method @Nullable public String getFeatureId(); + method @Nullable public String getAttributionTag(); method @NonNull public String getOp(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.SyncNotedAppOp> CREATOR; @@ -6856,9 +6856,9 @@ package android.app.admin { method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName); method @NonNull @WorkerThread public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String); method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName); - method public boolean getAutoTime(@NonNull android.content.ComponentName); + method public boolean getAutoTimeEnabled(@NonNull android.content.ComponentName); method @Deprecated public boolean getAutoTimeRequired(); - method public boolean getAutoTimeZone(@NonNull android.content.ComponentName); + method public boolean getAutoTimeZoneEnabled(@NonNull android.content.ComponentName); method @NonNull public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(@NonNull android.content.ComponentName); method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName); method public boolean getCameraDisabled(@Nullable android.content.ComponentName); @@ -6982,9 +6982,9 @@ package android.app.admin { method public boolean setApplicationHidden(@NonNull android.content.ComponentName, String, boolean); method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle); method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException; - method public void setAutoTime(@NonNull android.content.ComponentName, boolean); + method public void setAutoTimeEnabled(@NonNull android.content.ComponentName, boolean); method @Deprecated public void setAutoTimeRequired(@NonNull android.content.ComponentName, boolean); - method public void setAutoTimeZone(@NonNull android.content.ComponentName, boolean); + method public void setAutoTimeZoneEnabled(@NonNull android.content.ComponentName, boolean); method public void setBackupServiceEnabled(@NonNull android.content.ComponentName, boolean); method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean); method public void setCameraDisabled(@NonNull android.content.ComponentName, boolean); @@ -9696,7 +9696,7 @@ package android.content { method public abstract int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]); method public int delete(@NonNull android.net.Uri, @Nullable android.os.Bundle); method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]); - method @Nullable public final String getCallingFeatureId(); + method @Nullable public final String getCallingAttributionTag(); method @Nullable public final String getCallingPackage(); method @Nullable public final String getCallingPackageUnchecked(); method @Nullable public final android.content.Context getContext(); @@ -10030,11 +10030,11 @@ package android.content { method @CheckResult(suggest="#enforceUriPermission(Uri,int,int,String)") public abstract int checkUriPermission(android.net.Uri, int, int, int); method @CheckResult(suggest="#enforceUriPermission(Uri,String,String,int,int,int,String)") public abstract int checkUriPermission(@Nullable android.net.Uri, @Nullable String, @Nullable String, int, int, int); method @Deprecated public abstract void clearWallpaper() throws java.io.IOException; + method @NonNull public android.content.Context createAttributionContext(@Nullable String); method public abstract android.content.Context createConfigurationContext(@NonNull android.content.res.Configuration); method public abstract android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.Context createDeviceProtectedStorageContext(); method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display); - method @NonNull public android.content.Context createFeatureContext(@Nullable String); method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public android.content.Context createWindowContext(int, @Nullable android.os.Bundle); method public abstract String[] databaseList(); @@ -10052,6 +10052,7 @@ package android.content { method public abstract android.content.Context getApplicationContext(); method public abstract android.content.pm.ApplicationInfo getApplicationInfo(); method public abstract android.content.res.AssetManager getAssets(); + method @Nullable public String getAttributionTag(); method public abstract java.io.File getCacheDir(); method public abstract ClassLoader getClassLoader(); method public abstract java.io.File getCodeCacheDir(); @@ -10068,7 +10069,6 @@ package android.content { method @Nullable public abstract java.io.File getExternalFilesDir(@Nullable String); method public abstract java.io.File[] getExternalFilesDirs(String); method @Deprecated public abstract java.io.File[] getExternalMediaDirs(); - method @Nullable public String getFeatureId(); method public abstract java.io.File getFileStreamPath(String); method public abstract java.io.File getFilesDir(); method public java.util.concurrent.Executor getMainExecutor(); @@ -10806,6 +10806,7 @@ package android.content { field public static final String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE"; field public static final String EXTRA_TEXT = "android.intent.extra.TEXT"; field public static final String EXTRA_TIME = "android.intent.extra.TIME"; + field public static final String EXTRA_TIMEZONE = "time-zone"; field public static final String EXTRA_TITLE = "android.intent.extra.TITLE"; field public static final String EXTRA_UID = "android.intent.extra.UID"; field public static final String EXTRA_USER = "android.intent.extra.USER"; @@ -11438,6 +11439,7 @@ package android.content.pm { method public int describeContents(); method public void dump(android.util.Printer, String); method public static CharSequence getCategoryTitle(android.content.Context, int); + method @Nullable public Boolean isGwpAsanEnabled(); method public boolean isProfileableByShell(); method public boolean isResourceOverlay(); method public boolean isVirtualPreload(); @@ -11571,6 +11573,7 @@ package android.content.pm { method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle); method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles(); method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity); + method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity, @Nullable android.os.Bundle); method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); field public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED"; } @@ -26448,14 +26451,14 @@ package android.media { public static interface MediaParser.OutputConsumer { method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo); - method public void onSampleData(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException; - method public void onSeekMap(@NonNull android.media.MediaParser.SeekMap); - method public void onTrackData(int, @NonNull android.media.MediaParser.TrackData); - method public void onTracksFound(int); + method public void onSampleDataFound(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException; + method public void onSeekMapFound(@NonNull android.media.MediaParser.SeekMap); + method public void onTrackCountFound(int); + method public void onTrackDataFound(int, @NonNull android.media.MediaParser.TrackData); } public static final class MediaParser.SeekMap { - method public long getDurationUs(); + method public long getDurationMicros(); method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long); method public boolean isSeekable(); field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000 @@ -26464,7 +26467,7 @@ package android.media { public static final class MediaParser.SeekPoint { field @NonNull public static final android.media.MediaParser.SeekPoint START; field public final long position; - field public final long timeUs; + field public final long timeMicros; } public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader { @@ -40714,6 +40717,7 @@ package android.provider { field public static final String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update"; field public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url"; field public static final String RTT_CALLING_MODE = "rtt_calling_mode"; + field public static final String SECURE_FRP_MODE = "secure_frp_mode"; field public static final String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype"; field public static final String SETTINGS_CLASSNAME = "settings_classname"; field public static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints"; @@ -43012,12 +43016,12 @@ package android.service.autofill { } public static final class Dataset.Builder { - ctor public Dataset.Builder(@NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation); ctor public Dataset.Builder(@NonNull android.widget.RemoteViews); ctor public Dataset.Builder(); method @NonNull public android.service.autofill.Dataset build(); method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender); method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String); + method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation); method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue); method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews); method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern); @@ -45973,9 +45977,6 @@ package android.telecom { field public static final int MISSED = 5; // 0x5 field public static final int OTHER = 9; // 0x9 field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED"; - field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL"; - field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED"; - field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF"; field public static final int REJECTED = 6; // 0x6 field public static final int REMOTE = 3; // 0x3 field public static final int RESTRICTED = 8; // 0x8 @@ -46302,7 +46303,6 @@ package android.telecom { field public static final int DURATION_SHORT = 1; // 0x1 field public static final int DURATION_VERY_SHORT = 0; // 0x0 field public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER"; - field public static final String EXTRA_CALL_CREATED_TIME_MILLIS = "android.telecom.extra.CALL_CREATED_TIME_MILLIS"; field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE"; field public static final String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE"; field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION"; @@ -48044,13 +48044,13 @@ package android.telephony { method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>); method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context); method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList(); - method @Nullable public java.util.List<android.telephony.SubscriptionInfo> getActiveAndHiddenSubscriptionInfoList(); method public static int getActiveDataSubscriptionId(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getActiveSubscriptionInfoCount(); method public int getActiveSubscriptionInfoCountMax(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList(); + method @NonNull public java.util.List<android.telephony.SubscriptionInfo> getCompleteActiveSubscriptionInfoList(); method public static int getDefaultDataSubscriptionId(); method public static int getDefaultSmsSubscriptionId(); method public static int getDefaultSubscriptionId(); @@ -48213,6 +48213,7 @@ package android.telephony { method public boolean isEmergencyNumber(@NonNull String); method public boolean isHearingAidCompatibilitySupported(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed(); + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isModemEnabledForSlot(int); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int isMultiSimSupported(); method public boolean isNetworkRoaming(); method public boolean isRttSupported(); @@ -48326,7 +48327,6 @@ package android.telephony { field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0 field public static final int PHONE_TYPE_CDMA = 2; // 0x2 field public static final int PHONE_TYPE_GSM = 1; // 0x1 - field public static final int PHONE_TYPE_IMS = 5; // 0x5 field public static final int PHONE_TYPE_NONE = 0; // 0x0 field public static final int PHONE_TYPE_SIP = 3; // 0x3 field public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2; // 0x2 @@ -55582,10 +55582,12 @@ package android.view { } public interface WindowInsetsController { + method public void addOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener); method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener); method public int getSystemBarsAppearance(); method public int getSystemBarsBehavior(); method public void hide(int); + method public void removeOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener); method public void setSystemBarsAppearance(int, int); method public void setSystemBarsBehavior(int); method public void show(int); @@ -55596,6 +55598,10 @@ package android.view { field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2 } + public static interface WindowInsetsController.OnControllableInsetsChangedListener { + method public void onControllableInsetsChanged(@NonNull android.view.WindowInsetsController, int); + } + public interface WindowManager extends android.view.ViewManager { method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics(); method @Deprecated public android.view.Display getDefaultDisplay(); @@ -56735,6 +56741,7 @@ package android.view.contentcapture { method public final void notifySessionResumed(); method public final void notifyViewAppeared(@NonNull android.view.ViewStructure); method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId); + method public final void notifyViewInsetsChanged(@NonNull android.graphics.Insets); method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence); method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]); method public final void setContentCaptureContext(@Nullable android.view.contentcapture.ContentCaptureContext); @@ -82227,3 +82234,4 @@ package org.xmlpull.v1.sax2 { } } + diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt index 569e838ea177..83c78fe9578b 100644 --- a/api/lint-baseline.txt +++ b/api/lint-baseline.txt @@ -15,6 +15,16 @@ ArrayReturn: android.app.Notification.MessagingStyle.Message#getMessagesFromBund ArrayReturn: android.content.ContentProviderOperation#resolveExtrasBackReferences(android.content.ContentProviderResult[], int) parameter #0: +ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#SphericalCorrections(double[][], double[][]) parameter #0: + Method parameter should be Collection<> (or subclass) instead of raw array; was `double[][]` +ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#SphericalCorrections(double[][], double[][]) parameter #1: + Method parameter should be Collection<> (or subclass) instead of raw array; was `double[][]` +ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#getCorrectionUncertaintiesArray(): + Method should return Collection<> (or subclass) instead of raw array; was `double[][]` +ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#getCorrectionsArray(): + Method should return Collection<> (or subclass) instead of raw array; was `double[][]` +ArrayReturn: android.service.autofill.FillResponse.Builder#setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews, android.service.autofill.InlinePresentation) parameter #0: + Method parameter should be Collection<AutofillId> (or subclass) instead of raw array; was `android.view.autofill.AutofillId[]` BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED: @@ -453,8 +463,12 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface +ExecutorRegistration: android.media.MediaRouter2#setOnGetControllerHintsListener(android.media.MediaRouter2.OnGetControllerHintsListener): + Registration methods should have overload that accepts delivery Executor: `setOnGetControllerHintsListener` + + GenericException: android.content.res.loader.ResourcesProvider#finalize(): - Methods must not throw generic exceptions (`java.lang.Throwable`) + HiddenSuperclass: android.content.res.ColorStateList: @@ -499,6 +513,30 @@ HiddenSuperclass: android.util.StatsLog: +IntentBuilderName: android.net.VpnManager#provisionVpnProfile(android.net.PlatformVpnProfile): + Methods creating an Intent should be named `create<Foo>Intent()`, was `provisionVpnProfile` + + +KotlinOperator: android.media.AudioMetadata.Map#set(android.media.AudioMetadata.Key<T>, T): + Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object) +KotlinOperator: android.media.AudioMetadata.ReadMap#get(android.media.AudioMetadata.Key<T>): + Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object) + + +MethodNameUnits: android.media.MediaParser.SeekMap#getDurationMicros(): + Returned time values are strongly encouraged to be in milliseconds unless you need the extra precision, was `getDurationMicros` + + +MinMaxConstant: android.telephony.DataFailCause#MAX_ACCESS_PROBE: + If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_ACCESS_PROBE +MinMaxConstant: android.telephony.DataFailCause#MAX_IPV4_CONNECTIONS: + If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_IPV4_CONNECTIONS +MinMaxConstant: android.telephony.DataFailCause#MAX_IPV6_CONNECTIONS: + If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_IPV6_CONNECTIONS +MinMaxConstant: android.telephony.DataFailCause#MAX_PPP_INACTIVITY_TIMER_EXPIRED: + If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_PPP_INACTIVITY_TIMER_EXPIRED + + MissingNullability: android.app.AsyncNotedAppOp#equals(Object) parameter #0: MissingNullability: android.app.AsyncNotedAppOp#writeToParcel(android.os.Parcel, int) parameter #0: @@ -506,11 +544,11 @@ MissingNullability: android.app.AsyncNotedAppOp#writeToParcel(android.os.Parcel, MissingNullability: android.app.SyncNotedAppOp#equals(Object) parameter #0: MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#CHORASMIAN: - Missing nullability on field `CHORASMIAN` in class `class android.icu.lang.UCharacter.UnicodeBlock` + MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G: - Missing nullability on field `CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G` in class `class android.icu.lang.UCharacter.UnicodeBlock` + MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#DIVES_AKURU: - Missing nullability on field `DIVES_AKURU` in class `class android.icu.lang.UCharacter.UnicodeBlock` + MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS: MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#ELYMAIC: @@ -556,14 +594,39 @@ MissingNullability: android.icu.util.VersionInfo#UNICODE_12_0: MissingNullability: android.icu.util.VersionInfo#UNICODE_12_1: MissingNullability: android.icu.util.VersionInfo#UNICODE_13_0: - Missing nullability on field `UNICODE_13_0` in class `class android.icu.util.VersionInfo` + MissingNullability: android.media.MediaMetadataRetriever#getFrameAtTime(long, int, android.media.MediaMetadataRetriever.BitmapParams): MissingNullability: android.media.MediaMetadataRetriever#getScaledFrameAtTime(long, int, int, int, android.media.MediaMetadataRetriever.BitmapParams): - MissingNullability: java.time.chrono.JapaneseEra#REIWA: - Missing nullability on field `REIWA` in class `class java.time.chrono.JapaneseEra` + + + +NotCloseable: android.media.MediaCodec.GraphicBlock: + Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.media.MediaCodec.GraphicBlock +NotCloseable: android.media.MediaCodec.LinearBlock: + Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.media.MediaCodec.LinearBlock +NotCloseable: android.media.MediaParser: + Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.media.MediaParser +NotCloseable: android.media.MediaRouter2.RoutingController: + Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.media.MediaRouter2.RoutingController +NotCloseable: android.util.CloseGuard: + Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.util.CloseGuard +NotCloseable: android.view.SurfaceControlViewHost: + Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.view.SurfaceControlViewHost + + +OnNameExpected: android.app.admin.DevicePolicyKeyguardService#dismiss(): + If implemented by developer, should follow the on<Something> style; otherwise consider marking final +OnNameExpected: android.service.controls.ControlsProviderService#createPublisherFor(java.util.List<java.lang.String>): + Methods implemented by developers should follow the on<Something> style, was `createPublisherFor` +OnNameExpected: android.service.controls.ControlsProviderService#createPublisherForAllAvailable(): + Methods implemented by developers should follow the on<Something> style, was `createPublisherForAllAvailable` +OnNameExpected: android.service.controls.ControlsProviderService#createPublisherForSuggested(): + If implemented by developer, should follow the on<Something> style; otherwise consider marking final +OnNameExpected: android.service.controls.ControlsProviderService#performControlAction(String, android.service.controls.actions.ControlAction, java.util.function.Consumer<java.lang.Integer>): + Methods implemented by developers should follow the on<Something> style, was `performControlAction` RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler): @@ -1189,11 +1252,13 @@ SamShouldBeLast: android.location.LocationManager#registerGnssStatusCallback(jav SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener): SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener): + - +StreamFiles: android.content.res.loader.DirectoryAssetsProvider#DirectoryAssetsProvider(java.io.File): + Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryAssetsProvider(java.io.File) StreamFiles: android.content.res.loader.DirectoryResourceLoader#DirectoryResourceLoader(java.io.File): - Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryResourceLoader(java.io.File) + Todo: android.hardware.camera2.params.StreamConfigurationMap: diff --git a/api/removed.txt b/api/removed.txt index 8537b21eb438..077c915ef2dd 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -1,4 +1,12 @@ // Signature format: 2.0 +package android { + + public static final class R.attr { + field public static final int featureId = 16844301; // 0x101060d + } + +} + package android.app { public class ActivityManager { @@ -69,11 +77,17 @@ package android.app.usage { package android.content { + public abstract class ContentProvider implements android.content.ComponentCallbacks2 { + method @Deprecated @Nullable public final String getCallingFeatureId(); + } + public abstract class ContentResolver { method @Deprecated public void notifyChange(@NonNull Iterable<android.net.Uri>, @Nullable android.database.ContentObserver, int); } public abstract class Context { + method @Deprecated @NonNull public android.content.Context createFeatureContext(@Nullable String); + method @Deprecated @Nullable public String getFeatureId(); method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); method public abstract java.io.File getSharedPreferencesPath(String); } diff --git a/api/system-current.txt b/api/system-current.txt index 979d6e6c1488..a044888c0901 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -79,6 +79,7 @@ package android { field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER"; field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE"; field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED"; + field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS"; field public static final String FORCE_BACK = "android.permission.FORCE_BACK"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS"; @@ -231,6 +232,7 @@ package android { field public static final String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS"; field public static final String UPDATE_LOCK = "android.permission.UPDATE_LOCK"; field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES"; + field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS"; field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY"; field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS"; @@ -446,14 +448,37 @@ package android.app { field public static final int UID_STATE_TOP = 200; // 0xc8 } - public static final class AppOpsManager.HistoricalFeatureOps implements android.os.Parcelable { + public static final class AppOpsManager.AttributedHistoricalOps implements android.os.Parcelable { method public int describeContents(); - method @Nullable public String getFeatureId(); method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String); method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int); method @IntRange(from=0) public int getOpCount(); + method @Nullable public String getTag(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalFeatureOps> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedHistoricalOps> CREATOR; + } + + public static final class AppOpsManager.AttributedOpEntry implements android.os.Parcelable { + method public int describeContents(); + method public long getLastAccessBackgroundTime(int); + method public long getLastAccessForegroundTime(int); + method public long getLastAccessTime(int); + method public long getLastAccessTime(int, int, int); + method public long getLastBackgroundDuration(int); + method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int); + method public long getLastDuration(int); + method public long getLastDuration(int, int, int); + method public long getLastForegroundDuration(int); + method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int); + method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int); + method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int); + method public long getLastRejectBackgroundTime(int); + method public long getLastRejectForegroundTime(int); + method public long getLastRejectTime(int); + method public long getLastRejectTime(int, int, int); + method public boolean isRunning(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedOpEntry> CREATOR; } public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable { @@ -489,7 +514,7 @@ package android.app { public static final class AppOpsManager.HistoricalOpsRequest.Builder { ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build(); - method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFeatureId(@Nullable String); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setAttributionTag(@Nullable String); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String); @@ -498,9 +523,9 @@ package android.app { public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable { method public int describeContents(); - method @IntRange(from=0) public int getFeatureCount(); - method @Nullable public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOps(@NonNull String); - method @NonNull public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOpsAt(@IntRange(from=0) int); + method @Nullable public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOps(@NonNull String); + method @NonNull public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOpsAt(@IntRange(from=0) int); + method @IntRange(from=0) public int getAttributedOpsCount(); method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String); method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int); method @IntRange(from=0) public int getOpCount(); @@ -521,8 +546,8 @@ package android.app { public static final class AppOpsManager.OpEntry implements android.os.Parcelable { method public int describeContents(); + method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.AttributedOpEntry> getAttributedOpEntries(); method @Deprecated public long getDuration(); - method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.OpFeatureEntry> getFeatures(); method public long getLastAccessBackgroundTime(int); method public long getLastAccessForegroundTime(int); method public long getLastAccessTime(int); @@ -552,36 +577,13 @@ package android.app { public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable { method public int describeContents(); - method @Nullable public String getFeatureId(); + method @Nullable public String getAttributionTag(); method @Nullable public String getPackageName(); method @IntRange(from=0) public int getUid(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEventProxyInfo> CREATOR; } - public static final class AppOpsManager.OpFeatureEntry implements android.os.Parcelable { - method public int describeContents(); - method public long getLastAccessBackgroundTime(int); - method public long getLastAccessForegroundTime(int); - method public long getLastAccessTime(int); - method public long getLastAccessTime(int, int, int); - method public long getLastBackgroundDuration(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int); - method public long getLastDuration(int); - method public long getLastDuration(int, int, int); - method public long getLastForegroundDuration(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int); - method public long getLastRejectBackgroundTime(int); - method public long getLastRejectForegroundTime(int); - method public long getLastRejectTime(int); - method public long getLastRejectTime(int, int, int); - method public boolean isRunning(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpFeatureEntry> CREATOR; - } - public static final class AppOpsManager.PackageOps implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps(); @@ -686,7 +688,7 @@ package android.app { public final class RuntimeAppOpAccessMessage implements android.os.Parcelable { ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int); method public int describeContents(); - method @Nullable public String getFeatureId(); + method @Nullable public String getAttributionTag(); method @NonNull public String getMessage(); method @NonNull public String getOp(); method @NonNull public String getPackageName(); @@ -1369,7 +1371,6 @@ package android.app.role { method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @Nullable public String getDefaultSmsPackage(int); method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); @@ -2214,7 +2215,7 @@ package android.content.pm { field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery"; field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow"; field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock"; - field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000 + field public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = -268435456; // 0xf0000000 field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000 field public static final int FLAG_PERMISSION_AUTO_REVOKED = 131072; // 0x20000 field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20 @@ -2938,7 +2939,7 @@ package android.hardware.lights { public final class LightsManager.LightsSession implements java.lang.AutoCloseable { method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close(); - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest); } public final class LightsRequest { @@ -5618,8 +5619,8 @@ package android.media.tv.tuner.frontend { method public int getConstellation(); method public int getGuardInterval(); method public int getHierarchy(); - method public int getHpCodeRate(); - method public int getLpCodeRate(); + method public int getHighPriorityCodeRate(); + method public int getLowPriorityCodeRate(); method public int getPlpGroupId(); method public int getPlpId(); method public int getPlpMode(); @@ -5647,20 +5648,20 @@ package android.media.tv.tuner.frontend { field public static final int CODERATE_8_9 = 512; // 0x200 field public static final int CODERATE_AUTO = 1; // 0x1 field public static final int CODERATE_UNDEFINED = 0; // 0x0 + field public static final int CONSTELLATION_16QAM = 4; // 0x4 + field public static final int CONSTELLATION_256QAM = 16; // 0x10 + field public static final int CONSTELLATION_64QAM = 8; // 0x8 field public static final int CONSTELLATION_AUTO = 1; // 0x1 - field public static final int CONSTELLATION_CONSTELLATION_16QAM = 4; // 0x4 - field public static final int CONSTELLATION_CONSTELLATION_256QAM = 16; // 0x10 - field public static final int CONSTELLATION_CONSTELLATION_64QAM = 8; // 0x8 - field public static final int CONSTELLATION_CONSTELLATION_QPSK = 2; // 0x2 + field public static final int CONSTELLATION_QPSK = 2; // 0x2 field public static final int CONSTELLATION_UNDEFINED = 0; // 0x0 + field public static final int GUARD_INTERVAL_19_128 = 64; // 0x40 + field public static final int GUARD_INTERVAL_19_256 = 128; // 0x80 + field public static final int GUARD_INTERVAL_1_128 = 32; // 0x20 + field public static final int GUARD_INTERVAL_1_16 = 4; // 0x4 + field public static final int GUARD_INTERVAL_1_32 = 2; // 0x2 + field public static final int GUARD_INTERVAL_1_4 = 16; // 0x10 + field public static final int GUARD_INTERVAL_1_8 = 8; // 0x8 field public static final int GUARD_INTERVAL_AUTO = 1; // 0x1 - field public static final int GUARD_INTERVAL_INTERVAL_19_128 = 64; // 0x40 - field public static final int GUARD_INTERVAL_INTERVAL_19_256 = 128; // 0x80 - field public static final int GUARD_INTERVAL_INTERVAL_1_128 = 32; // 0x20 - field public static final int GUARD_INTERVAL_INTERVAL_1_16 = 4; // 0x4 - field public static final int GUARD_INTERVAL_INTERVAL_1_32 = 2; // 0x2 - field public static final int GUARD_INTERVAL_INTERVAL_1_4 = 16; // 0x10 - field public static final int GUARD_INTERVAL_INTERVAL_1_8 = 8; // 0x8 field public static final int GUARD_INTERVAL_UNDEFINED = 0; // 0x0 field public static final int HIERARCHY_1_INDEPTH = 64; // 0x40 field public static final int HIERARCHY_1_NATIVE = 4; // 0x4 @@ -5695,8 +5696,8 @@ package android.media.tv.tuner.frontend { method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setGuardInterval(int); method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHierarchy(int); method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHighPriority(boolean); - method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHpCodeRate(int); - method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setLpCodeRate(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHighPriorityCodeRate(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setLowPriorityCodeRate(int); method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setMiso(boolean); method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setPlpGroupId(int); method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setPlpId(int); @@ -7156,7 +7157,7 @@ package android.net.wifi { method @NonNull public java.util.List<android.net.MacAddress> getBlockedClientList(); method public int getChannel(); method public int getMaxNumberOfClients(); - method public int getShutdownTimeoutMillis(); + method public long getShutdownTimeoutMillis(); method public boolean isAutoShutdownEnabled(); method public boolean isClientControlByUserEnabled(); method @Nullable public android.net.wifi.WifiConfiguration toWifiConfiguration(); @@ -7170,16 +7171,17 @@ package android.net.wifi { ctor public SoftApConfiguration.Builder(); ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration); method @NonNull public android.net.wifi.SoftApConfiguration build(); - method @NonNull public android.net.wifi.SoftApConfiguration.Builder enableClientControlByUser(boolean); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAllowedClientList(@NonNull java.util.List<android.net.MacAddress>); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAutoShutdownEnabled(boolean); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBlockedClientList(@NonNull java.util.List<android.net.MacAddress>); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int); - method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientList(@NonNull java.util.List<android.net.MacAddress>, @NonNull java.util.List<android.net.MacAddress>); + 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 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) int); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) long); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String); } @@ -7240,7 +7242,6 @@ package android.net.wifi { field @Deprecated public int numScorerOverride; field @Deprecated public int numScorerOverrideAndSwitchedNetwork; field @Deprecated public boolean requirePmf; - field @Deprecated @Nullable public String saePasswordId; field @Deprecated public boolean shared; field @Deprecated public boolean useExternalScores; } @@ -7338,7 +7339,7 @@ package android.net.wifi { method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>); method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>); method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks(); - method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration(); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public android.net.wifi.SoftApConfiguration getSoftApConfiguration(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener); method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState(); @@ -8223,13 +8224,15 @@ package android.os { field public static final String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS"; field public static final String ACTION_UPDATE_CONVERSATION_ACTIONS = "android.intent.action.UPDATE_CONVERSATION_ACTIONS"; field public static final String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS"; - field public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB"; + field @RequiresPermission("android.permission.UPDATE_CONFIG") public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB"; field public static final String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL"; field public static final String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID"; field public static final String ACTION_UPDATE_NETWORK_WATCHLIST = "android.intent.action.UPDATE_NETWORK_WATCHLIST"; field public static final String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS"; field public static final String ACTION_UPDATE_SMART_SELECTION = "android.intent.action.UPDATE_SMART_SELECTION"; field public static final String ACTION_UPDATE_SMS_SHORT_CODES = "android.intent.action.UPDATE_SMS_SHORT_CODES"; + field public static final String EXTRA_REQUIRED_HASH = "android.os.extra.REQUIRED_HASH"; + field public static final String EXTRA_VERSION = "android.os.extra.VERSION"; } public class Environment { @@ -8939,12 +8942,12 @@ package android.permission { method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>); method @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable); - method @BinderThread public void onUpdateUserSensitivePermissionFlags(); + method @BinderThread public void onUpdateUserSensitivePermissionFlags(int, @NonNull Runnable); field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; } public final class PermissionManager { - method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public int getRuntimePermissionsVersion(); + method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion(); method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions(); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledCarrierApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledImsServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); @@ -8952,7 +8955,7 @@ package android.permission { method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); + method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int); method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String); } @@ -9066,18 +9069,6 @@ package android.printservice.recommendation { package android.provider { - public class BlockedNumberContract { - field public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact"; - field public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number"; - field public static final String RES_BLOCK_STATUS = "block_status"; - field public static final int STATUS_BLOCKED_IN_LIST = 1; // 0x1 - field public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5; // 0x5 - field public static final int STATUS_BLOCKED_PAYPHONE = 4; // 0x4 - field public static final int STATUS_BLOCKED_RESTRICTED = 2; // 0x2 - field public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3; // 0x3 - field public static final int STATUS_NOT_BLOCKED = 0; // 0x0 - } - @Deprecated public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns { field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata"; field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata"; @@ -9389,7 +9380,6 @@ package android.provider { field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications"; field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications"; field public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled"; - field public static final String SECURE_FRP_MODE = "secure_frp_mode"; field public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES = "theme_customization_overlay_packages"; field public static final String USER_SETUP_COMPLETE = "user_setup_complete"; field public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10; // 0xa @@ -9435,20 +9425,12 @@ package android.provider { public static final class Telephony.Carriers implements android.provider.BaseColumns { field public static final String APN_SET_ID = "apn_set_id"; - field @Deprecated public static final String BEARER_BITMASK = "bearer_bitmask"; field public static final int CARRIER_EDITED = 4; // 0x4 - field @NonNull public static final android.net.Uri DPC_URI; field public static final String EDITED_STATUS = "edited"; - field public static final int INVALID_APN_ID = -1; // 0xffffffff field public static final String MAX_CONNECTIONS = "max_conns"; field public static final String MODEM_PERSIST = "modem_cognitive"; field public static final String MTU = "mtu"; field public static final int NO_APN_SET_ID = 0; // 0x0 - field public static final String PROFILE_ID = "profile_id"; - field public static final String SKIP_464XLAT = "skip_464xlat"; - field public static final int SKIP_464XLAT_DEFAULT = -1; // 0xffffffff - field public static final int SKIP_464XLAT_DISABLE = 0; // 0x0 - field public static final int SKIP_464XLAT_ENABLE = 1; // 0x1 field public static final String TIME_LIMIT_FOR_MAX_CONNECTIONS = "max_conns_time"; field public static final int UNEDITED = 0; // 0x0 field public static final int USER_DELETED = 2; // 0x2 @@ -9813,7 +9795,7 @@ package android.service.autofill { public static final class Dataset.Builder { ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation); - method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation); + method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation); } public abstract class InlineSuggestionRenderService extends android.app.Service { @@ -9974,7 +9956,7 @@ package android.service.dataloader { } public static final class DataLoaderService.FileSystemConnector { - method public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException; + method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException; } } @@ -10652,14 +10634,7 @@ package android.telecom { } public final class PhoneAccount implements android.os.Parcelable { - field public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 128; // 0x80 - field public static final int CAPABILITY_EMERGENCY_PREFERRED = 8192; // 0x2000 - field public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 512; // 0x200 field public static final int CAPABILITY_MULTI_USER = 32; // 0x20 - field public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE"; - field public static final String EXTRA_PLAY_CALL_RECORDING_TONE = "android.telecom.extra.PLAY_CALL_RECORDING_TONE"; - field public static final String EXTRA_SORT_ORDER = "android.telecom.extra.SORT_ORDER"; - field public static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK = "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK"; } public static class PhoneAccount.Builder { @@ -10745,20 +10720,13 @@ package android.telecom { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle); - field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED"; - field public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED"; field public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1; // 0x1 field public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2; // 0x2 field public static final int CALL_SOURCE_UNSPECIFIED = 0; // 0x0 field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT"; - field public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE"; - field public static final String EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE"; field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT"; field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE"; - field public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE"; field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL"; - field public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE"; - field public static final String EXTRA_UNKNOWN_CALL_HANDLE = "android.telecom.extra.UNKNOWN_CALL_HANDLE"; field public static final int TTY_MODE_FULL = 1; // 0x1 field public static final int TTY_MODE_HCO = 2; // 0x2 field public static final int TTY_MODE_OFF = 0; // 0x0 @@ -11206,19 +11174,6 @@ package android.telephony { field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 } - public final class PinResult implements android.os.Parcelable { - ctor public PinResult(int, int); - method public int describeContents(); - method public int getAttemptsRemaining(); - method @NonNull public static android.telephony.PinResult getDefaultFailedResult(); - method public int getType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PinResult> CREATOR; - field public static final int PIN_RESULT_TYPE_FAILURE = 2; // 0x2 - field public static final int PIN_RESULT_TYPE_INCORRECT = 1; // 0x1 - field public static final int PIN_RESULT_TYPE_SUCCESS = 0; // 0x0 - } - public final class PreciseCallState implements android.os.Parcelable { ctor public PreciseCallState(int, int, int, int, int); method public int describeContents(); @@ -11349,7 +11304,6 @@ package android.telephony { public class ServiceState implements android.os.Parcelable { method public void fillInNotifierBundle(@NonNull android.os.Bundle); method public int getDataNetworkType(); - method public int getDataRegistrationState(); method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int); method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int); method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int); @@ -11583,7 +11537,6 @@ package android.telephony { } public class TelephonyManager { - method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting); method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int changeIccLockPassword(@NonNull String, @NonNull String); method public int checkCarrierPrivilegesForPackage(String); @@ -11616,11 +11569,9 @@ package android.telephony { method @Deprecated public boolean getDataEnabled(); method @Deprecated public boolean getDataEnabled(int); method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean); - method @NonNull public static String getDefaultSimCountryIso(); - method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode(); - method public int getEmergencyNumberDbVersion(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getIsimImpu(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst(); @@ -11650,7 +11601,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataAllowedInVoiceCall(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionEnabled(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed(); method public boolean isDataConnectivityPossible(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled(); @@ -11658,7 +11609,6 @@ package android.telephony { method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled(); - method public boolean isModemEnabledForSlot(int); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String); @@ -11668,7 +11618,6 @@ package android.telephony { method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String); - method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting); method public boolean needsOtaServiceProvisioning(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyUserActivity(); @@ -11711,10 +11660,8 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoiceActivationState(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void shutdownAllRadios(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPin(String); - method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult supplyPinReportPinResult(@NonNull String); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String, String); - method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult supplyPukReportPinResult(@NonNull String, @NonNull String); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String, String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff(); @@ -11807,7 +11754,6 @@ package android.telephony { field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L - field public static final int PHONE_TYPE_THIRD_PARTY = 4; // 0x4 field public static final int RADIO_POWER_OFF = 0; // 0x0 field public static final int RADIO_POWER_ON = 1; // 0x1 field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2 @@ -11887,23 +11833,6 @@ package android.telephony.cdma { package android.telephony.data { - public class ApnSetting implements android.os.Parcelable { - method @NonNull public static String getApnTypesStringFromBitmask(int); - field public static final String TYPE_ALL_STRING = "*"; - field public static final String TYPE_CBS_STRING = "cbs"; - field public static final String TYPE_DEFAULT_STRING = "default"; - field public static final String TYPE_DUN_STRING = "dun"; - field public static final String TYPE_EMERGENCY_STRING = "emergency"; - field public static final String TYPE_FOTA_STRING = "fota"; - field public static final String TYPE_HIPRI_STRING = "hipri"; - field public static final String TYPE_IA_STRING = "ia"; - field public static final String TYPE_IMS_STRING = "ims"; - field public static final String TYPE_MCX_STRING = "mcx"; - field public static final String TYPE_MMS_STRING = "mms"; - field public static final String TYPE_SUPL_STRING = "supl"; - field public static final String TYPE_XCAP_STRING = "xcap"; - } - public final class DataCallResponse implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.List<android.net.LinkAddress> getAddresses(); @@ -13262,6 +13191,7 @@ package android.view.contentcapture { method public long getEventTime(); method @Nullable public android.view.autofill.AutofillId getId(); method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds(); + method @Nullable public android.graphics.Insets getInsets(); method @Nullable public CharSequence getText(); method public int getType(); method @Nullable public android.view.contentcapture.ViewNode getViewNode(); @@ -13272,6 +13202,7 @@ package android.view.contentcapture { field public static final int TYPE_SESSION_RESUMED = 7; // 0x7 field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2 + field public static final int TYPE_VIEW_INSETS_CHANGED = 9; // 0x9 field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3 field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5 field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4 diff --git a/api/test-current.txt b/api/test-current.txt index 9d284b59cdb3..1403b69afca3 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -21,6 +21,7 @@ package android { field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS"; field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS"; + field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS"; field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG"; field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE"; field public static final String WRITE_OBB = "android.permission.WRITE_OBB"; @@ -82,6 +83,7 @@ package android.app { method public static void resumeAppSwitches() throws android.os.RemoteException; method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int); method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle); + method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String); field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7 field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1 field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6 @@ -262,14 +264,37 @@ package android.app { field public static final int UID_STATE_TOP = 200; // 0xc8 } - public static final class AppOpsManager.HistoricalFeatureOps implements android.os.Parcelable { + public static final class AppOpsManager.AttributedHistoricalOps implements android.os.Parcelable { method public int describeContents(); - method @Nullable public String getFeatureId(); method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String); method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int); method @IntRange(from=0) public int getOpCount(); + method @Nullable public String getTag(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalFeatureOps> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedHistoricalOps> CREATOR; + } + + public static final class AppOpsManager.AttributedOpEntry implements android.os.Parcelable { + method public int describeContents(); + method public long getLastAccessBackgroundTime(int); + method public long getLastAccessForegroundTime(int); + method public long getLastAccessTime(int); + method public long getLastAccessTime(int, int, int); + method public long getLastBackgroundDuration(int); + method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int); + method public long getLastDuration(int); + method public long getLastDuration(int, int, int); + method public long getLastForegroundDuration(int); + method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int); + method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int); + method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int); + method public long getLastRejectBackgroundTime(int); + method public long getLastRejectForegroundTime(int); + method public long getLastRejectTime(int); + method public long getLastRejectTime(int, int, int); + method public boolean isRunning(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedOpEntry> CREATOR; } public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable { @@ -310,7 +335,7 @@ package android.app { public static final class AppOpsManager.HistoricalOpsRequest.Builder { ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build(); - method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFeatureId(@Nullable String); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setAttributionTag(@Nullable String); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String); @@ -319,9 +344,9 @@ package android.app { public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable { method public int describeContents(); - method @IntRange(from=0) public int getFeatureCount(); - method @Nullable public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOps(@NonNull String); - method @NonNull public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOpsAt(@IntRange(from=0) int); + method @Nullable public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOps(@NonNull String); + method @NonNull public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOpsAt(@IntRange(from=0) int); + method @IntRange(from=0) public int getAttributedOpsCount(); method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String); method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int); method @IntRange(from=0) public int getOpCount(); @@ -342,8 +367,8 @@ package android.app { public static final class AppOpsManager.OpEntry implements android.os.Parcelable { method public int describeContents(); + method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.AttributedOpEntry> getAttributedOpEntries(); method @Deprecated public long getDuration(); - method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.OpFeatureEntry> getFeatures(); method public long getLastAccessBackgroundTime(int); method public long getLastAccessForegroundTime(int); method public long getLastAccessTime(int); @@ -373,36 +398,13 @@ package android.app { public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable { method public int describeContents(); - method @Nullable public String getFeatureId(); + method @Nullable public String getAttributionTag(); method @Nullable public String getPackageName(); method @IntRange(from=0) public int getUid(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEventProxyInfo> CREATOR; } - public static final class AppOpsManager.OpFeatureEntry implements android.os.Parcelable { - method public int describeContents(); - method public long getLastAccessBackgroundTime(int); - method public long getLastAccessForegroundTime(int); - method public long getLastAccessTime(int); - method public long getLastAccessTime(int, int, int); - method public long getLastBackgroundDuration(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int); - method public long getLastDuration(int); - method public long getLastDuration(int, int, int); - method public long getLastForegroundDuration(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int); - method public long getLastRejectBackgroundTime(int); - method public long getLastRejectForegroundTime(int); - method public long getLastRejectTime(int); - method public long getLastRejectTime(int, int, int); - method public boolean isRunning(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpFeatureEntry> CREATOR; - } - public static final class AppOpsManager.PackageOps implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps(); @@ -427,6 +429,7 @@ package android.app { method public boolean isBlockableSystem(); method public boolean isImportanceLockedByCriticalDeviceFunction(); method public boolean isImportanceLockedByOEM(); + method public void lockFields(int); method public void setBlockableSystem(boolean); method public void setDeleted(boolean); method public void setFgServiceShown(boolean); @@ -434,6 +437,7 @@ package android.app { method public void setImportanceLockedByOEM(boolean); method public void setImportantConversation(boolean); method public void setOriginalImportance(int); + field public static final int USER_LOCKED_SOUND = 32; // 0x20 } public final class NotificationChannelGroup implements android.os.Parcelable { @@ -462,7 +466,7 @@ package android.app { public final class RuntimeAppOpAccessMessage implements android.os.Parcelable { ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int); method public int describeContents(); - method @Nullable public String getFeatureId(); + method @Nullable public String getAttributionTag(); method @NonNull public String getMessage(); method @NonNull public String getOp(); method @NonNull public String getPackageName(); @@ -1278,7 +1282,7 @@ package android.hardware.lights { public final class LightsManager.LightsSession implements java.lang.AutoCloseable { method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close(); - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest); } public final class LightsRequest { @@ -2811,9 +2815,9 @@ package android.permission { } public final class PermissionManager { - method @IntRange(from=0) @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public int getRuntimePermissionsVersion(); + method @IntRange(from=0) @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion(); method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions(); - method @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); + method @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); } public static final class PermissionManager.SplitPermissionInfo { @@ -3131,7 +3135,7 @@ package android.service.autofill { public static final class Dataset.Builder { ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation); - method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation); + method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation); } public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation { @@ -3742,7 +3746,7 @@ package android.telephony { method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean); method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context); - method public int getEmergencyNumberDbVersion(); + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getEmergencyNumberDbVersion(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag(); method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion(); method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting); @@ -4967,7 +4971,7 @@ package android.view { method public void resetRtlProperties(); method public boolean restoreFocusInCluster(int); method public boolean restoreFocusNotInCluster(); - method public void setAutofilled(boolean); + method public void setAutofilled(boolean, boolean); method public final void setFocusedInCluster(); method public void setIsRootNamespace(boolean); method public final void setShowingLayoutBounds(boolean); @@ -5081,6 +5085,7 @@ package android.view.contentcapture { method public long getEventTime(); method @Nullable public android.view.autofill.AutofillId getId(); method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds(); + method @Nullable public android.graphics.Insets getInsets(); method @Nullable public CharSequence getText(); method public int getType(); method @Nullable public android.view.contentcapture.ViewNode getViewNode(); @@ -5091,6 +5096,7 @@ package android.view.contentcapture { field public static final int TYPE_SESSION_RESUMED = 7; // 0x7 field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2 + field public static final int TYPE_VIEW_INSETS_CHANGED = 9; // 0x9 field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3 field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5 field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4 diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index f84e4b5f2575..8f5e49d5b428 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -113,7 +113,7 @@ Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path, Status Idmap2Service::createIdmap(const std::string& target_apk_path, const std::string& overlay_apk_path, int32_t fulfilled_policies, bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED, - std::unique_ptr<std::string>* _aidl_return) { + aidl::nullable<std::string>* _aidl_return) { assert(_aidl_return); SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path; _aidl_return->reset(nullptr); @@ -155,7 +155,7 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, return error("failed to write to idmap path " + idmap_path); } - *_aidl_return = std::make_unique<std::string>(idmap_path); + *_aidl_return = aidl::make_nullable<std::string>(idmap_path); return ok(); } diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h index 94d2af49260c..b6f5136fc801 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -19,9 +19,7 @@ #include <android-base/unique_fd.h> #include <binder/BinderService.h> - -#include <memory> -#include <string> +#include <binder/Nullable.h> #include "android/os/BnIdmap2.h" @@ -46,7 +44,7 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 { binder::Status createIdmap(const std::string& target_apk_path, const std::string& overlay_apk_path, int32_t fulfilled_policies, bool enforce_overlayable, int32_t user_id, - std::unique_ptr<std::string>* _aidl_return) override; + aidl::nullable<std::string>* _aidl_return) override; }; } // namespace android::os diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index bd6ca47cb4b8..33bfe512cc10 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -113,8 +113,8 @@ cc_defaults { "libbase", "libcutils", "libprotoutil", - "libstatslog", "libstatsmetadata", + "libstatslog_statsd", "libsysutils", "libutils", ], @@ -171,6 +171,37 @@ cc_library_static { ], } +genrule { + name: "statslog_statsd.h", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsd.h --module statsd --namespace android,os,statsd,util", + out: [ + "statslog_statsd.h", + ], +} + +genrule { + name: "statslog_statsd.cpp", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsd.cpp --module statsd --namespace android,os,statsd,util --importHeader statslog_statsd.h", + out: [ + "statslog_statsd.cpp", + ], +} + +cc_library_static { + name: "libstatslog_statsd", + generated_sources: ["statslog_statsd.cpp"], + generated_headers: ["statslog_statsd.h"], + export_generated_headers: ["statslog_statsd.h"], + apex_available: [ + "com.android.os.statsd", + "test_com.android.os.statsd", + ], + shared_libs: [ + "libstatssocket", + ] +} // ========= // statsd @@ -300,6 +331,7 @@ cc_test { static_libs: [ "libgmock", "libplatformprotos", + "libstatslog", //TODO(b/150976524): remove this when the tests no longer hardcode atoms. "libstatssocket_private", ], diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 649c00428028..69b9fc7ad9a0 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -34,7 +34,7 @@ #include "state/StateManager.h" #include "stats_log_util.h" #include "stats_util.h" -#include "statslog.h" +#include "statslog_statsd.h" #include "storage/StorageManager.h" using namespace android; @@ -287,17 +287,17 @@ void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(bool is_rollback, int64_t firstId = trainInfo->experimentIds.at(0); auto& ids = trainInfo->experimentIds; switch (trainInfo->status) { - case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS: + case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS: if (find(ids.begin(), ids.end(), firstId + 1) == ids.end()) { ids.push_back(firstId + 1); } break; - case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED: + case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED: if (find(ids.begin(), ids.end(), firstId + 2) == ids.end()) { ids.push_back(firstId + 2); } break; - case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS: + case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS: if (find(ids.begin(), ids.end(), firstId + 3) == ids.end()) { ids.push_back(firstId + 3); } @@ -366,13 +366,13 @@ vector<int64_t> StatsLogProcessor::processWatchdogRollbackOccurred(const int32_t int64_t firstId = trainInfoOnDisk.experimentIds[0]; auto& ids = trainInfoOnDisk.experimentIds; switch (rollbackTypeIn) { - case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE: + case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE: if (find(ids.begin(), ids.end(), firstId + 4) == ids.end()) { ids.push_back(firstId + 4); } StorageManager::writeTrainInfo(trainInfoOnDisk); break; - case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS: + case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS: if (find(ids.begin(), ids.end(), firstId + 5) == ids.end()) { ids.push_back(firstId + 5); } @@ -405,13 +405,13 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { // Hard-coded logic to update train info on disk and fill in any information // this log event may be missing. - if (event->GetTagId() == android::util::BINARY_PUSH_STATE_CHANGED) { + if (event->GetTagId() == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) { onBinaryPushStateChangedEventLocked(event); } // Hard-coded logic to update experiment ids on disk for certain rollback // types and fill the rollback atom with experiment ids - if (event->GetTagId() == android::util::WATCHDOG_ROLLBACK_OCCURRED) { + if (event->GetTagId() == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) { onWatchdogRollbackOccurredLocked(event); } @@ -429,7 +429,7 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { // Hard-coded logic to update the isolated uid's in the uid-map. // The field numbers need to be currently updated by hand with atoms.proto - if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) { + if (event->GetTagId() == android::os::statsd::util::ISOLATED_UID_CHANGED) { onIsolatedUidChangedEventLocked(*event); } @@ -446,7 +446,7 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { } - if (event->GetTagId() != android::util::ISOLATED_UID_CHANGED) { + if (event->GetTagId() != android::os::statsd::util::ISOLATED_UID_CHANGED) { // Map the isolated uid to host uid if necessary. mapIsolatedUidToHostUidIfNecessaryLocked(event); } diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index cd10457dcd40..07579bb21860 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -32,7 +32,7 @@ #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h> #include <frameworks/base/cmds/statsd/src/uid_data.pb.h> #include <private/android_filesystem_config.h> -#include <statslog.h> +#include <statslog_statsd.h> #include <stdio.h> #include <stdlib.h> #include <sys/system_properties.h> @@ -767,7 +767,8 @@ status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& ar } if (good) { dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state); - android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED, uid, label, state); + android::os::statsd::util::stats_write( + android::os::statsd::util::APP_BREADCRUMB_REPORTED, uid, label, state); } else { print_cmd_help(out); return UNKNOWN_ERROR; @@ -1188,7 +1189,7 @@ Status StatsService::unsetBroadcastSubscriber(int64_t configId, Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) { // Permission check not necessary as it's meant for applications to write to // statsd. - android::util::stats_write(util::APP_BREADCRUMB_REPORTED, + android::os::statsd::util::stats_write(android::os::statsd::util::APP_BREADCRUMB_REPORTED, (int32_t) AIBinder_getCallingUid(), label, state); return Status::ok(); diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp index 019a9f7e5f9a..5722f923d11e 100644 --- a/cmds/statsd/src/anomaly/AlarmTracker.cpp +++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp @@ -23,7 +23,6 @@ #include "stats_util.h" #include "storage/StorageManager.h" -#include <statslog.h> #include <time.h> namespace android { diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index 7ace44eef564..a21abbf042cb 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -25,7 +25,7 @@ #include "subscriber/SubscriberReporter.h" #include <inttypes.h> -#include <statslog.h> +#include <statslog_statsd.h> #include <time.h> namespace android { @@ -235,8 +235,8 @@ void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id()); // TODO(b/110564268): This should also take in the const MetricDimensionKey& key? - android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(), - mConfigKey.GetId(), mAlert.id()); + util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(), + mConfigKey.GetId(), mAlert.id()); } void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs, diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 80f9fea6a2fb..bd5bdc6c4f2f 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -122,10 +122,10 @@ message Atom { SettingChanged setting_changed = 41 [(module) = "framework"]; ActivityForegroundStateChanged activity_foreground_state_changed = 42 [(module) = "framework"]; - IsolatedUidChanged isolated_uid_changed = 43 [(module) = "framework"]; + IsolatedUidChanged isolated_uid_changed = 43 [(module) = "framework", (module) = "statsd"]; PacketWakeupOccurred packet_wakeup_occurred = 44 [(module) = "framework"]; WallClockTimeShifted wall_clock_time_shifted = 45 [(module) = "framework"]; - AnomalyDetected anomaly_detected = 46; + AnomalyDetected anomaly_detected = 46 [(module) = "statsd"]; AppBreadcrumbReported app_breadcrumb_reported = 47 [(allow_from_any_uid) = true, (module) = "statsd"]; AppStartOccurred app_start_occurred = 48 [(module) = "framework"]; @@ -138,7 +138,7 @@ message Atom { AppStartMemoryStateCaptured app_start_memory_state_captured = 55 [(module) = "framework"]; ShutdownSequenceReported shutdown_sequence_reported = 56 [(module) = "framework"]; BootSequenceReported boot_sequence_reported = 57; - DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true]; + DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true, (module) = "statsd"]; OverlayStateChanged overlay_state_changed = 59 [(module) = "framework"]; ForegroundServiceStateChanged foreground_service_state_changed = 60 [(module) = "framework"]; @@ -166,7 +166,7 @@ message Atom { LowMemReported low_mem_reported = 81 [(module) = "framework"]; GenericAtom generic_atom = 82; KeyValuePairsAtom key_value_pairs_atom = - 83 [(allow_from_any_uid) = true, (module) = "framework"]; + 83 [(allow_from_any_uid) = true, (module) = "framework", (module) = "statsd"]; VibratorStateChanged vibrator_state_changed = 84 [(module) = "framework"]; DeferredJobStatsReported deferred_job_stats_reported = 85 [(module) = "framework"]; ThermalThrottlingStateChanged thermal_throttling = 86 [deprecated=true]; @@ -242,7 +242,8 @@ message Atom { AdbConnectionChanged adb_connection_changed = 144 [(module) = "framework"]; SpeechDspStatReported speech_dsp_stat_reported = 145; UsbContaminantReported usb_contaminant_reported = 146 [(module) = "framework"]; - WatchdogRollbackOccurred watchdog_rollback_occurred = 147 [(module) = "framework"]; + WatchdogRollbackOccurred watchdog_rollback_occurred = + 147 [(module) = "framework", (module) = "statsd"]; BiometricSystemHealthIssueDetected biometric_system_health_issue_detected = 148 [(module) = "framework"]; BubbleUIChanged bubble_ui_changed = 149 [(module) = "sysui"]; @@ -401,7 +402,7 @@ message Atom { } // Pulled events will start at field 10000. - // Next: 10076 + // Next: 10080 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"]; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"]; @@ -412,7 +413,8 @@ message Atom { SubsystemSleepState subsystem_sleep_state = 10005; CpuTimePerFreq cpu_time_per_freq = 10008 [(module) = "framework"]; CpuTimePerUid cpu_time_per_uid = 10009 [(module) = "framework"]; - CpuTimePerUidFreq cpu_time_per_uid_freq = 10010 [(module) = "framework"]; + CpuTimePerUidFreq cpu_time_per_uid_freq = + 10010 [(module) = "framework", (module) = "statsd"]; WifiActivityInfo wifi_activity_info = 10011 [(module) = "framework"]; ModemActivityInfo modem_activity_info = 10012 [(module) = "framework"]; BluetoothActivityInfo bluetooth_activity_info = 10007 [(module) = "framework"]; @@ -425,9 +427,9 @@ message Atom { RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"]; FullBatteryCapacity full_battery_capacity = 10020 [(module) = "framework"]; Temperature temperature = 10021 [(module) = "framework"]; - BinderCalls binder_calls = 10022 [(module) = "framework"]; + BinderCalls binder_calls = 10022 [(module) = "framework", (module) = "statsd"]; BinderCallsExceptions binder_calls_exceptions = 10023 [(module) = "framework"]; - LooperStats looper_stats = 10024 [(module) = "framework"]; + LooperStats looper_stats = 10024 [(module) = "framework", (module) = "statsd"]; DiskStats disk_stats = 10025 [(module) = "framework"]; DirectoryUsage directory_usage = 10026 [(module) = "framework"]; AppSize app_size = 10027 [(module) = "framework"]; @@ -455,7 +457,7 @@ message Atom { NumFacesEnrolled num_faces_enrolled = 10048 [(module) = "framework"]; RoleHolder role_holder = 10049 [(module) = "framework"]; DangerousPermissionState dangerous_permission_state = 10050 [(module) = "framework"]; - TrainInfo train_info = 10051; + TrainInfo train_info = 10051 [(module) = "statsd"]; TimeZoneDataInfo time_zone_data_info = 10052 [(module) = "framework"]; ExternalStorageInfo external_storage_info = 10053 [(module) = "framework"]; GpuStatsGlobalInfo gpu_stats_global_info = 10054; @@ -483,7 +485,11 @@ message Atom { PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences = 10073 [(module) = "framework"]; GnssStats gnss_stats = 10074 [(module) = "framework"]; - AppFeaturesOps app_features_ops = 10075 [(module) = "framework"]; + AttributedAppOps attributed_app_ops = 10075 [(module) = "framework"]; + VoiceCallSession voice_call_session = 10076 [(module) = "telephony"]; + VoiceCallRatUsage voice_call_rat_usage = 10077 [(module) = "telephony"]; + SimSlotState sim_slot_state = 10078 [(module) = "telephony"]; + SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"]; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -4475,8 +4481,7 @@ message BootTimeEventErrorCode { * after an OTA. * * Logged from: - * - system/core/fs_mgr/libsnapshot/snapshot.cpp - * - system/core/fs_mgr/libsnapshot/snapshotctl.cpp + * - system/update_engine/cleanup_previous_update_action.cc */ message SnapshotMergeReported { // Keep in sync with @@ -7598,18 +7603,19 @@ message AppOps { } /** - * Historical app ops data per package and features. + * Historical app ops data per package and attribution tag. */ -message AppFeaturesOps { +message AttributedAppOps { // Uid of the package requesting the op optional int32 uid = 1 [(is_uid) = true]; // Name of the package performing the op optional string package_name = 2; - // feature id; provided by developer when accessing related API, limited at 50 chars by API. - // Features must be provided through manifest using <feature> tag available in R and above. - optional string feature_id = 3; + // tag; provided by developer when accessing related API, limited at 50 chars by API. + // Attributions must be provided through manifest using <attribution> tag available in R and + // above. + optional string tag = 3; // operation id; maps to the OPSTR_* constants in AppOpsManager.java optional string op = 4; @@ -8471,9 +8477,11 @@ message RuntimeAppOpAccess { // operation string id per OPSTR_ constants in AppOpsManager.java optional string op = 3; - // feature id; provided by developer when accessing related API, limited at 50 chars by API. - // Features must be provided through manifest using <feature> tag available in R and above. - optional string feature_id = 4; + // attribution_tag; provided by developer when accessing related API, limited at 50 chars by + // API. + // Attributions must be provided through manifest using <attribution> tag available in R and + // above. + optional string attribution_tag = 4; // message related to app op access, limited to 600 chars by API optional string message = 5; @@ -8670,6 +8678,154 @@ message AppFreezeChanged { } /** + * Pulls information for a single voice call. + * + * Each pull creates multiple atoms, one for each call. The sequence is randomized when pulled. + * + * Pulled from: + * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/PersistPullers.java + */ +message VoiceCallSession { + // Bearer (IMS or CS) when the call started. + optional android.telephony.CallBearerEnum bearer_at_start = 1; + + // Bearer (IMS or CS) when the call ended. + // The bearer may change during the call, e.g. due to SRVCC. + optional android.telephony.CallBearerEnum bearer_at_end = 2; + + // Direction of the call (incoming or outgoing). + optional android.telephony.CallDirectionEnum direction = 3; + + // Time spent setting up the call. + optional android.telephony.CallSetupDurationEnum setup_duration = 4; + + // Whether the call ended before the setup was completed. + optional bool setup_failed = 5; + + // IMS reason code or CS disconnect cause. + // For IMS, see: frameworks/base/telephony/java/android/telephony/ims/ImsReasonInfo.java + // For CS, see: frameworks/base/telephony/java/android/telephony/DisconnectCause.java + optional int32 disconnect_reason_code = 6; + + // IMS extra code or CS precise disconnect cause. + // For IMS, this code is vendor-specific + // For CS, see: frameworks/base/telephony/java/android/telephony/PreciseDisconnectCause.java + optional int32 disconnect_extra_code = 7; + + // IMS extra message or CS vendor cause. + optional string disconnect_extra_message = 8; + + // Radio access technology (RAT) used when call started. + optional android.telephony.NetworkTypeEnum rat_at_start = 9; + + // Radio access technology (RAT) used when call terminated. + optional android.telephony.NetworkTypeEnum rat_at_end = 10; + + // Number of times RAT changed during the call. + optional int64 rat_switch_count = 11; + + // A bitmask of all codecs used during the call. + // See: frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java + optional int64 codec_bitmask = 12; + + // Number of other calls going on during call setup, for the same SIM slot. + optional int32 concurrent_call_count_at_start = 13; + + // Number of other calls going on during call termination, for the same SIM slot. + optional int32 concurrent_call_count_at_end = 14; + + // Index of the SIM is used, 0 for single-SIM devices. + optional int32 sim_slot_index = 15; + + // Whether the device was in multi-SIM mode (with multiple active SIM profiles). + optional bool is_multi_sim = 16; + + // Whether the call was made with an eSIM profile. + optional bool is_esim = 17; + + // Carrier ID of the SIM card. + // See https://source.android.com/devices/tech/config/carrierid. + optional int32 carrier_id = 18; + + // Whether an SRVCC has been completed successfully. + // SRVCC (CS fallback) should be recorded in the IMS call since there will be no more SRVCC + // events once the call is switched to CS. + optional bool srvcc_completed = 19; + + // Number of SRVCC failures. + optional int64 srvcc_failure_count = 20; + + // Number of SRVCC cancellations. + optional int64 srvcc_cancellation_count = 21; + + // Whether the Real-Time Text (RTT) was ever used in the call. + optional bool rtt_enabled = 22; + + // Whether this was an emergency call. + optional bool is_emergency = 23; + + // Whether the call was performed while roaming. + optional bool is_roaming = 24; +} + +/** + * Pulls voice call radio access technology (RAT) usage. + * + * Each pull creates multiple atoms, one for each carrier/RAT, the order of which is irrelevant to + * time. The atom will be skipped if not enough data is available. + * + * Pulled from: + * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/PersistPullers.java + */ +message VoiceCallRatUsage { + // Carrier ID (https://source.android.com/devices/tech/config/carrierid). + optional int32 carrier_id = 1; + + // Radio access technology. + optional android.telephony.NetworkTypeEnum rat = 2; + + // Total duration that voice calls spent on this carrier and RAT. + optional int64 total_duration_seconds = 3; + + // Total number of calls using this carrier and RAT. + // A call is counted once even if it used the RAT multiple times. + optional int64 call_count = 4; +} + +/** + * Pulls the number of active SIM slots and SIMs/eSIM profiles. + * + * Pulled from: + * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/NonPersistPullers.java + */ +message SimSlotState { + // Number of active SIM slots (both physical and eSIM profiles) in the device. + optional int32 active_slot_count = 1; + + // Number of SIM cards (both physical and active eSIM profiles). + // This number is always equal to or less than the number of active SIM slots. + optional int32 sim_count = 2; + + // Number of active eSIM profiles. + // This number is always equal to or less than the number of SIMs. + optional int32 esim_count = 3; +} + +/** + * Pulls supported cellular radio access technologies. + * + * This atom reports the capabilities of the device, rather than the network it has access to. + * + * Pulled from: + * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/NonPersistPullers.java + */ +message SupportedRadioAccessFamily { + // A bitmask of supported radio technologies. + // See android.telephony.TelephonyManager.NetworkTypeBitMask. + optional int64 network_type_bitmask = 1; +} + +/** * Logs gnss stats from location service provider * * Pulled from: diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 0115ba2b6bc0..8b6a5a17bc0e 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -32,7 +32,7 @@ #include "../statscompanion_util.h" #include "StatsCallbackPuller.h" #include "TrainInfoPuller.h" -#include "statslog.h" +#include "statslog_statsd.h" using std::shared_ptr; using std::vector; @@ -47,7 +47,7 @@ const int64_t NO_ALARM_UPDATE = INT64_MAX; StatsPullerManager::StatsPullerManager() : kAllPullAtomInfo({ // TrainInfo. - {{.atomTag = android::util::TRAIN_INFO}, new TrainInfoPuller()}, + {{.atomTag = util::TRAIN_INFO}, new TrainInfoPuller()}, }), mNextPullTimeNs(NO_ALARM_UPDATE) { } diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp index a7d8d4ebebae..3837f4a1a517 100644 --- a/cmds/statsd/src/external/TrainInfoPuller.cpp +++ b/cmds/statsd/src/external/TrainInfoPuller.cpp @@ -22,7 +22,7 @@ #include "TrainInfoPuller.h" #include "logd/LogEvent.h" #include "stats_log_util.h" -#include "statslog.h" +#include "statslog_statsd.h" #include "storage/StorageManager.h" using std::make_shared; @@ -33,7 +33,7 @@ namespace os { namespace statsd { TrainInfoPuller::TrainInfoPuller() : - StatsPuller(android::util::TRAIN_INFO) { + StatsPuller(util::TRAIN_INFO) { } bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 3054b6d2204b..2bd13d7ad81b 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -20,7 +20,7 @@ #include <android/util/ProtoOutputStream.h> #include "../stats_log_util.h" -#include "statslog.h" +#include "statslog_statsd.h" #include "storage/StorageManager.h" namespace android { @@ -113,9 +113,9 @@ const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID = 1; const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME = 2; const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = { - {android::util::BINDER_CALLS, {6000, 10000}}, - {android::util::LOOPER_STATS, {1500, 2500}}, - {android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}}, + {util::BINDER_CALLS, {6000, 10000}}, + {util::LOOPER_STATS, {1500, 2500}}, + {util::CPU_TIME_PER_UID_FREQ, {6000, 10000}}, }; StatsdStats::StatsdStats() { diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 974e203cd612..258f84d0fb79 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -18,7 +18,7 @@ #include "logd/LogEvent.h" #include "stats_log_util.h" -#include "statslog.h" +#include "statslog_statsd.h" #include <android/binder_ibinder.h> #include <android-base/stringprintf.h> @@ -100,7 +100,7 @@ LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedT const std::map<int32_t, float>& float_map) { mLogdTimestampNs = wallClockTimestampNs; mElapsedTimestampNs = elapsedTimestampNs; - mTagId = android::util::KEY_VALUE_PAIRS_ATOM; + mTagId = util::KEY_VALUE_PAIRS_ATOM; mLogUid = uid; int pos[] = {1, 1, 1}; @@ -153,7 +153,7 @@ LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requi const std::vector<uint8_t>& experimentIds, int32_t userId) { mLogdTimestampNs = getWallClockNs(); mElapsedTimestampNs = getElapsedRealtimeNs(); - mTagId = android::util::BINARY_PUSH_STATE_CHANGED; + mTagId = util::BINARY_PUSH_STATE_CHANGED; mLogUid = AIBinder_getCallingUid(); mLogPid = AIBinder_getCallingPid(); @@ -172,7 +172,7 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const InstallTrainInfo& trainInfo) { mLogdTimestampNs = wallClockTimestampNs; mElapsedTimestampNs = elapsedTimestampNs; - mTagId = android::util::TRAIN_INFO; + mTagId = util::TRAIN_INFO; mValues.push_back( FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode))); diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 3940aa8e1243..b68eeb8d6499 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -328,4 +328,3 @@ void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::v } // namespace statsd } // namespace os } // namespace android - diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 536700f3bfe7..6f54ea7d86c2 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -31,7 +31,7 @@ #include "state/StateManager.h" #include "stats_log_util.h" #include "stats_util.h" -#include "statslog.h" +#include "statslog_statsd.h" using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_INT32; @@ -291,7 +291,7 @@ bool MetricsManager::checkLogCredentials(const LogEvent& event) { } bool MetricsManager::eventSanityCheck(const LogEvent& event) { - if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) { + if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) { // Check that app breadcrumb reported fields are valid. status_t err = NO_ERROR; @@ -318,7 +318,7 @@ bool MetricsManager::eventSanityCheck(const LogEvent& event) { VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState); return false; } - } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) { + } else if (event.GetTagId() == util::DAVEY_OCCURRED) { // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp. // Check that the davey duration is reasonable. Max length check is for privacy. status_t err = NO_ERROR; diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index 4e3c506e1812..02fe7b1f5003 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -139,7 +139,7 @@ public: // record is deleted. void appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set, bool includeVersionStrings, bool includeInstaller, - util::ProtoOutputStream* proto); + ProtoOutputStream* proto); // Forces the output to be cleared. We still generate a snapshot based on the current state. // This results in extra data uploaded but helps us reconstruct the uid mapping on the server diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 4b78e305f65c..7febb35355a3 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -137,7 +137,9 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { simplePredicate, trackerNameIndexMap); EXPECT_FALSE(conditionTracker.isSliced()); - LogEvent event(1 /*tagId*/, 0 /*timestamp*/); + // This event is not accessed in this test besides dimensions which is why this is okay. + // This is technically an invalid LogEvent because we do not call parseBuffer. + LogEvent event(/*uid=*/0, /*pid=*/0); vector<MatchingState> matcherState; matcherState.push_back(MatchingState::kNotMatched); @@ -222,7 +224,9 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { trackerNameIndexMap); EXPECT_FALSE(conditionTracker.isSliced()); - LogEvent event(1 /*tagId*/, 0 /*timestamp*/); + // This event is not accessed in this test besides dimensions which is why this is okay. + // This is technically an invalid LogEvent because we do not call parseBuffer. + LogEvent event(/*uid=*/0, /*pid=*/0); // one matched start vector<MatchingState> matcherState; @@ -296,8 +300,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { std::vector<int> uids = {111, 222, 333}; - LogEvent event(/*uid=*/-1, /*pid=*/-1); - makeWakeLockEvent(&event, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/1); + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/1); // one matched start vector<MatchingState> matcherState; @@ -308,7 +312,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); vector<bool> changedCache(1, false); - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, + conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache, changedCache); if (position == Position::FIRST || position == Position::LAST) { @@ -333,7 +337,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); // another wake lock acquired by this uid - LogEvent event2(/*uid=*/-1, /*pid=*/-1); + LogEvent event2(/*uid=*/0, /*pid=*/0); makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/1); matcherState.clear(); matcherState.push_back(MatchingState::kMatched); @@ -353,7 +357,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { // wake lock 1 release - LogEvent event3(/*uid=*/-1, /*pid=*/-1); + LogEvent event3(/*uid=*/0, /*pid=*/0); makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/0); matcherState.clear(); matcherState.push_back(MatchingState::kNotMatched); @@ -372,7 +376,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - LogEvent event4(/*uid=*/-1, /*pid=*/-1); + LogEvent event4(/*uid=*/0, /*pid=*/0); makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/0); matcherState.clear(); matcherState.push_back(MatchingState::kNotMatched); @@ -399,246 +403,235 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { } -//TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { -// std::vector<sp<ConditionTracker>> allConditions; -// -// SimplePredicate simplePredicate = getWakeLockHeldCondition( -// true /*nesting*/, true /*default to false*/, false /*slice output by uid*/, -// Position::ANY /* position */); -// string conditionName = "WL_HELD"; -// -// unordered_map<int64_t, int> trackerNameIndexMap; -// trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; -// trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; -// trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; -// -// SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), -// 0 /*condition tracker index*/, simplePredicate, -// trackerNameIndexMap); -// -// EXPECT_FALSE(conditionTracker.isSliced()); -// -// std::vector<int> uid_list1 = {111, 1111, 11111}; -// string uid1_wl1 = "wl1_1"; -// std::vector<int> uid_list2 = {222, 2222, 22222}; -// string uid2_wl1 = "wl2_1"; -// -// LogEvent event(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event, uid_list1, uid1_wl1, 1); -// -// // one matched start for uid1 -// vector<MatchingState> matcherState; -// matcherState.push_back(MatchingState::kMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// vector<sp<ConditionTracker>> allPredicates; -// vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); -// vector<bool> changedCache(1, false); -// -// conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, -// changedCache); -// -// EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); -// EXPECT_TRUE(changedCache[0]); -// -// // Now test query -// ConditionKey queryKey; -// conditionCache[0] = ConditionState::kNotEvaluated; -// -// conditionTracker.isConditionMet(queryKey, allPredicates, -// true, -// conditionCache); -// EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); -// -// // another wake lock acquired by this uid -// LogEvent event2(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event2, uid_list2, uid2_wl1, 1); -// matcherState.clear(); -// matcherState.push_back(MatchingState::kMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// conditionCache[0] = ConditionState::kNotEvaluated; -// changedCache[0] = false; -// conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, -// changedCache); -// EXPECT_FALSE(changedCache[0]); -// -// // uid1 wake lock 1 release -// LogEvent event3(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event3, uid_list1, uid1_wl1, 0); // now release it. -// matcherState.clear(); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kMatched); -// conditionCache[0] = ConditionState::kNotEvaluated; -// changedCache[0] = false; -// conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, -// changedCache); -// // nothing changes, because uid2 is still holding wl. -// EXPECT_FALSE(changedCache[0]); -// -// LogEvent event4(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event4, uid_list2, uid2_wl1, 0); // now release it. -// matcherState.clear(); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kMatched); -// conditionCache[0] = ConditionState::kNotEvaluated; -// changedCache[0] = false; -// conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache, -// changedCache); -// EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); -// EXPECT_TRUE(changedCache[0]); -// -// // query again -// conditionCache[0] = ConditionState::kNotEvaluated; -// conditionTracker.isConditionMet(queryKey, allPredicates, -// true, -// conditionCache); -// EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); -//} -// -//TEST(SimpleConditionTrackerTest, TestStopAll) { -// std::vector<sp<ConditionTracker>> allConditions; -// for (Position position : -// { Position::FIRST, Position::LAST }) { -// SimplePredicate simplePredicate = getWakeLockHeldCondition( -// true /*nesting*/, true /*default to false*/, true /*output slice by uid*/, -// position); -// string conditionName = "WL_HELD_BY_UID3"; -// -// unordered_map<int64_t, int> trackerNameIndexMap; -// trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; -// trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; -// trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; -// -// SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), -// 0 /*condition tracker index*/, simplePredicate, -// trackerNameIndexMap); -// -// std::vector<int> uid_list1 = {111, 1111, 11111}; -// std::vector<int> uid_list2 = {222, 2222, 22222}; -// -// LogEvent event(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event, uid_list1, "wl1", 1); -// -// // one matched start -// vector<MatchingState> matcherState; -// matcherState.push_back(MatchingState::kMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// vector<sp<ConditionTracker>> allPredicates; -// vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); -// vector<bool> changedCache(1, false); -// -// conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, -// changedCache); -// if (position == Position::FIRST || -// position == Position::LAST) { -// EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); -// } else { -// EXPECT_EQ(uid_list1.size(), conditionTracker.mSlicedConditionState.size()); -// } -// EXPECT_TRUE(changedCache[0]); -// { -// if (position == Position::FIRST || -// position == Position::LAST) { -// EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); -// } else { -// EXPECT_EQ(uid_list1.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); -// } -// } -// -// // Now test query -// const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName); -// conditionCache[0] = ConditionState::kNotEvaluated; -// -// conditionTracker.isConditionMet(queryKey, allPredicates, -// false, -// conditionCache); -// EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); -// -// // another wake lock acquired by uid2 -// LogEvent event2(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event2, uid_list2, "wl2", 1); -// matcherState.clear(); -// matcherState.push_back(MatchingState::kMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// conditionCache[0] = ConditionState::kNotEvaluated; -// changedCache[0] = false; -// conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, -// changedCache); -// if (position == Position::FIRST || -// position == Position::LAST) { -// EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size()); -// } else { -// EXPECT_EQ(uid_list1.size() + uid_list2.size(), -// conditionTracker.mSlicedConditionState.size()); -// } -// EXPECT_TRUE(changedCache[0]); -// { -// if (position == Position::FIRST || -// position == Position::LAST) { -// EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); -// } else { -// EXPECT_EQ(uid_list2.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); -// } -// } -// -// -// // TEST QUERY -// const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName); -// conditionCache[0] = ConditionState::kNotEvaluated; -// conditionTracker.isConditionMet(queryKey, allPredicates, -// false, -// conditionCache); -// -// EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); -// -// -// // stop all event -// LogEvent event3(2 /*tagId*/, 0 /*timestamp*/); -// matcherState.clear(); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kMatched); -// -// conditionCache[0] = ConditionState::kNotEvaluated; -// changedCache[0] = false; -// conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, -// changedCache); -// EXPECT_TRUE(changedCache[0]); -// EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); -// { -// if (position == Position::FIRST || position == Position::LAST) { -// EXPECT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); -// } else { -// EXPECT_EQ(uid_list1.size() + uid_list2.size(), -// conditionTracker.getChangedToFalseDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); -// } -// } -// -// // TEST QUERY -// const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName); -// conditionCache[0] = ConditionState::kNotEvaluated; -// conditionTracker.isConditionMet(queryKey, allPredicates, -// false, -// conditionCache); -// EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); -// -// // TEST QUERY -// const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName); -// conditionCache[0] = ConditionState::kNotEvaluated; -// conditionTracker.isConditionMet(queryKey, allPredicates, -// false, -// conditionCache); -// EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); -// } -//} +TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { + std::vector<sp<ConditionTracker>> allConditions; + + SimplePredicate simplePredicate = + getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/, + false /*slice output by uid*/, Position::ANY /* position */); + string conditionName = "WL_HELD"; + + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; + trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; + trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; + + SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), + 0 /*condition tracker index*/, simplePredicate, + trackerNameIndexMap); + + EXPECT_FALSE(conditionTracker.isSliced()); + + std::vector<int> uids1 = {111, 1111, 11111}; + string uid1_wl1 = "wl1_1"; + std::vector<int> uids2 = {222, 2222, 22222}; + string uid2_wl1 = "wl2_1"; + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1, /*acquire=*/1); + + // one matched start for uid1 + vector<MatchingState> matcherState; + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + vector<sp<ConditionTracker>> allPredicates; + vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); + vector<bool> changedCache(1, false); + + conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache, + changedCache); + + EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); + EXPECT_TRUE(changedCache[0]); + + // Now test query + ConditionKey queryKey; + conditionCache[0] = ConditionState::kNotEvaluated; + + conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache); + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + + // another wake lock acquired by this uid + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1, /*acquire=*/1); + + matcherState.clear(); + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_FALSE(changedCache[0]); + + // uid1 wake lock 1 release + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1, + /*release=*/0); // now release it. + + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, + changedCache); + // nothing changes, because uid2 is still holding wl. + EXPECT_FALSE(changedCache[0]); + + LogEvent event4(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1, + /*acquire=*/0); // now release it. + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); + EXPECT_TRUE(changedCache[0]); + + // query again + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); +} + +TEST(SimpleConditionTrackerTest, TestStopAll) { + std::vector<sp<ConditionTracker>> allConditions; + for (Position position : {Position::FIRST, Position::LAST}) { + SimplePredicate simplePredicate = + getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/, + true /*output slice by uid*/, position); + string conditionName = "WL_HELD_BY_UID3"; + + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; + trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; + trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; + + SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), + 0 /*condition tracker index*/, simplePredicate, + trackerNameIndexMap); + + std::vector<int> uids1 = {111, 1111, 11111}; + std::vector<int> uids2 = {222, 2222, 22222}; + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, "wl1", /*acquire=*/1); + + // one matched start + vector<MatchingState> matcherState; + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + vector<sp<ConditionTracker>> allPredicates; + vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); + vector<bool> changedCache(1, false); + + conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache, + changedCache); + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); + } else { + EXPECT_EQ(uids1.size(), conditionTracker.mSlicedConditionState.size()); + } + EXPECT_TRUE(changedCache[0]); + { + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(uids1.size(), + conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } + } + + // Now test query + const auto queryKey = getWakeLockQueryKey(position, uids1, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + + conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + + // another wake lock acquired by uid2 + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1); + + matcherState.clear(); + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, + changedCache); + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size()); + } else { + EXPECT_EQ(uids1.size() + uids2.size(), conditionTracker.mSlicedConditionState.size()); + } + EXPECT_TRUE(changedCache[0]); + { + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(uids2.size(), + conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } + } + + // TEST QUERY + const auto queryKey2 = getWakeLockQueryKey(position, uids2, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); + + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + + // stop all event + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1); + + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kMatched); + + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_TRUE(changedCache[0]); + EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); + { + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(uids1.size() + uids2.size(), + conditionTracker.getChangedToFalseDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); + } + } + + // TEST QUERY + const auto queryKey3 = getWakeLockQueryKey(position, uids1, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); + + // TEST QUERY + const auto queryKey4 = getWakeLockQueryKey(position, uids2, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); + } +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp index 1eaaf08ab2b9..e0eebefd1621 100644 --- a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp @@ -53,185 +53,192 @@ StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) { } // namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) { -// const int num_buckets = 1; -// const int threshold = 3; -// auto config = CreateStatsdConfig(num_buckets, threshold); -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); -// -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); -// -// sp<AnomalyTracker> anomalyTracker = -// processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; -// -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// std::vector<AttributionNodeInternal> attributions2 = { -// CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")}; -// std::vector<AttributionNodeInternal> attributions3 = { -// CreateAttribution(111, "App1"), CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions4 = { -// CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions5 = { -// CreateAttribution(222, "GMSCoreModule1") }; -// -// FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)111)); -// HashableDimensionKey whatKey1({fieldValue1}); -// MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); -// -// FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)222)); -// HashableDimensionKey whatKey2({fieldValue2}); -// MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); -// -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 3); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// event = CreateAcquireWakelockEvent(attributions3, "wl1", bucketStartTimeNs + 4); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 4); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// // Fired alarm and refractory period end timestamp updated. -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 5); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 100); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + bucketSizeNs + 1); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 3); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 4); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -//} -// -//TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) { -// const int num_buckets = 3; -// const int threshold = 3; -// auto config = CreateStatsdConfig(num_buckets, threshold); -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); -// -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); -// -// sp<AnomalyTracker> anomalyTracker = -// processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; -// -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// std::vector<AttributionNodeInternal> attributions2 = { -// CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")}; -// std::vector<AttributionNodeInternal> attributions3 = { -// CreateAttribution(111, "App1"), CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions4 = { -// CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions5 = { -// CreateAttribution(222, "GMSCoreModule1") }; -// -// FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)111)); -// HashableDimensionKey whatKey1({fieldValue1}); -// MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); -// -// FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)222)); -// HashableDimensionKey whatKey2({fieldValue2}); -// MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); -// -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// // Fired alarm and refractory period end timestamp updated. -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 4); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 1); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -//} +TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) { + const int num_buckets = 1; + const int threshold = 3; + auto config = CreateStatsdConfig(num_buckets, threshold); + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); + + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + std::vector<int> attributionUids1 = {111}; + std::vector<string> attributionTags1 = {"App1"}; + std::vector<int> attributionUids2 = {111, 222}; + std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"}; + std::vector<int> attributionUids3 = {111, 333}; + std::vector<string> attributionTags3 = {"App1", "App3"}; + std::vector<int> attributionUids4 = {222, 333}; + std::vector<string> attributionTags4 = {"GMSCoreModule1", "App3"}; + std::vector<int> attributionUids5 = {222}; + std::vector<string> attributionTags5 = {"GMSCoreModule1"}; + + FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)111)); + HashableDimensionKey whatKey1({fieldValue1}); + MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); + + FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)222)); + HashableDimensionKey whatKey2({fieldValue2}); + MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids4, attributionTags4, + "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids5, attributionTags5, + "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids3, attributionTags3, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids5, attributionTags5, + "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + // Fired alarm and refractory period end timestamp updated. + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 5, attributionUids1, attributionTags1, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 100, attributionUids1, attributionTags1, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids4, + attributionTags4, "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids5, + attributionTags5, "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 3, attributionUids5, + attributionTags5, "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 4, attributionUids5, + attributionTags5, "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); +} + +TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) { + const int num_buckets = 3; + const int threshold = 3; + auto config = CreateStatsdConfig(num_buckets, threshold); + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); + + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + std::vector<int> attributionUids1 = {111}; + std::vector<string> attributionTags1 = {"App1"}; + std::vector<int> attributionUids2 = {111, 222}; + std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"}; + + FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)111)); + HashableDimensionKey whatKey1({fieldValue1}); + MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Fired alarm and refractory period end timestamp updated. + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids1, attributionTags1, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids2, + attributionTags2, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, attributionUids2, + attributionTags2, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 2, attributionUids2, + attributionTags2, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp index 03a209a0e41f..fe45b5507c25 100644 --- a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp @@ -69,414 +69,432 @@ StatsdConfig CreateStatsdConfig(int num_buckets, return config; } -} // namespace - -std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"), - CreateAttribution(222, "GMSCoreModule1")}; +std::vector<int> attributionUids1 = {111, 222}; +std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1"}; -std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"), - CreateAttribution(222, "GMSCoreModule1")}; +std::vector<int> attributionUids2 = {111, 222}; +std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1"}; -std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1")}; +std::vector<int> attributionUids3 = {222}; +std::vector<string> attributionTags3 = {"GMSCoreModule1"}; -MetricDimensionKey dimensionKey( - HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED, - (int32_t)0x02010101), Value((int32_t)111))}), - DEFAULT_DIMENSION_KEY); +MetricDimensionKey dimensionKey1( + HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED, + (int32_t)0x02010101), + Value((int32_t)111))}), + DEFAULT_DIMENSION_KEY); MetricDimensionKey dimensionKey2( HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), Value((int32_t)222))}), DEFAULT_DIMENSION_KEY); -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) { -// const int num_buckets = 1; -// const uint64_t threshold_ns = NS_PER_SEC; -// auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); -// -// int64_t bucketStartTimeNs = 10 * NS_PER_SEC; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); -// -// sp<AnomalyTracker> anomalyTracker = -// processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; -// -// auto screen_on_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1); -// auto screen_off_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 10); -// processor->OnLogEvent(screen_on_event.get()); -// processor->OnLogEvent(screen_off_event.get()); -// -// // Acquire wakelock wl1. -// auto acquire_event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 11); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event. -// auto release_event = CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 101); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire wakelock wl1 within bucket #0. -// acquire_event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 110); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Release wakelock wl1. One anomaly detected. -// release_event = CreateReleaseWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + NS_PER_SEC + 109); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire wakelock wl1. -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + NS_PER_SEC + 112); -// processor->OnLogEvent(acquire_event.get()); -// // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the -// // end of the refractory period. -// const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, -// (uint32_t)alarmFiredTimestampSec0); -// -// // Anomaly alarm fired. -// auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( -// static_cast<uint32_t>(alarmFiredTimestampSec0)); -// EXPECT_EQ(1u, alarmSet.size()); -// processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Release wakelock wl1. -// release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// // Within refractory period. No more anomaly detected. -// EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire wakelock wl1. -// acquire_event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11); -// processor->OnLogEvent(acquire_event.get()); -// const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey); -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC, -// (uint64_t)alarmFiredTimestampSec1); -// -// // Release wakelock wl1. -// release_event = CreateReleaseWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( -// static_cast<uint32_t>(alarmFiredTimestampSec1)); -// EXPECT_EQ(0u, alarmSet.size()); -// -// // Acquire wakelock wl1 near the end of bucket #0. -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 2); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// // Release the event at early bucket #1. -// release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// // Anomaly detected when stopping the alarm. The refractory period does not change. -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Condition changes to false. -// screen_on_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 2 * bucketSizeNs + 20); -// processor->OnLogEvent(screen_on_event.get()); -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// acquire_event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 30); -// processor->OnLogEvent(acquire_event.get()); -// // The condition is false. Do not start the alarm. -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Condition turns true. -// screen_off_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC); -// processor->OnLogEvent(screen_off_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// // Condition turns to false. -// screen_on_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1); -// processor->OnLogEvent(screen_on_event.get()); -// // Condition turns to false. Cancelled the alarm. -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// // Detected one anomaly. -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Condition turns to true again. -// screen_off_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2); -// processor->OnLogEvent(screen_off_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// release_event = CreateReleaseWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -//} -// -//TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) { -// const int num_buckets = 3; -// const uint64_t threshold_ns = NS_PER_SEC; -// auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); -// -// int64_t bucketStartTimeNs = 10 * NS_PER_SEC; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); -// -// sp<AnomalyTracker> anomalyTracker = -// processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; -// -// auto screen_off_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1); -// processor->OnLogEvent(screen_off_event.get()); -// -// // Acquire wakelock "wc1" in bucket #0. -// auto acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Release wakelock "wc1" in bucket #0. -// auto release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire wakelock "wc1" in bucket #1. -// acquire_event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 1); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// release_event = CreateReleaseWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 100); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire wakelock "wc2" in bucket #2. -// acquire_event = CreateAcquireWakelockEvent( -// attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2, -// anomalyTracker->getAlarmTimestampSec(dimensionKey2)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// // Release wakelock "wc2" in bucket #2. -// release_event = CreateReleaseWakelockEvent( -// attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// // Acquire wakelock "wc1" in bucket #2. -// acquire_event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Release wakelock "wc1" in bucket #2. -// release_event = CreateReleaseWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + -// (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// acquire_event = CreateAcquireWakelockEvent( -// attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4); -// processor->OnLogEvent(acquire_event.get()); -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey2)); -// -// release_event = CreateReleaseWakelockEvent( -// attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs + 2); -// processor->OnLogEvent(release_event.get()); -// release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs + 6); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); -// // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered. -// EXPECT_EQ(refractory_period_sec + -// (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -//} -// -//TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) { -// const int num_buckets = 2; -// const uint64_t threshold_ns = 3 * NS_PER_SEC; -// auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false); -// int64_t bucketStartTimeNs = 10 * NS_PER_SEC; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; -// -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC; -// config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec); -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); -// -// sp<AnomalyTracker> anomalyTracker = -// processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; -// -// auto screen_off_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1); -// processor->OnLogEvent(screen_off_event.get()); -// -// // Acquire wakelock "wc1" in bucket #0. -// auto acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 100); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire the wakelock "wc1" again. -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1); -// processor->OnLogEvent(acquire_event.get()); -// // The alarm does not change. -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Anomaly alarm fired late. -// const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC; -// auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( -// static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC)); -// EXPECT_EQ(1u, alarmSet.size()); -// processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs - 100); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// auto release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 1); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// // Within the refractory period. No anomaly. -// EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // A new wakelock, but still within refractory period. -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC); -// // Still in the refractory period. No anomaly. -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -//} +} // namespace + +TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) { + const int num_buckets = 1; + const uint64_t threshold_ns = NS_PER_SEC; + auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); + + int64_t bucketStartTimeNs = 10 * NS_PER_SEC; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + auto screen_on_event = CreateScreenStateChangedEvent( + bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + auto screen_off_event = CreateScreenStateChangedEvent( + bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + processor->OnLogEvent(screen_on_event.get()); + processor->OnLogEvent(screen_off_event.get()); + + // Acquire wakelock wl1. + auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 11, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event. + auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 101, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire wakelock wl1 within bucket #0. + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 110, attributionUids2, + attributionTags2, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Release wakelock wl1. One anomaly detected. + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 109, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire wakelock wl1. + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 112, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the + // end of the refractory period. + const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, + (uint32_t)alarmFiredTimestampSec0); + + // Anomaly alarm fired. + auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( + static_cast<uint32_t>(alarmFiredTimestampSec0)); + EXPECT_EQ(1u, alarmSet.size()); + processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Release wakelock wl1. + release_event = + CreateReleaseWakelockEvent(alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + // Within refractory period. No more anomaly detected. + EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire wakelock wl1. + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(acquire_event.get()); + const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1); + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC, + (uint64_t)alarmFiredTimestampSec1); + + // Release wakelock wl1. + release_event = + CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + + (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( + static_cast<uint32_t>(alarmFiredTimestampSec1)); + EXPECT_EQ(0u, alarmSet.size()); + + // Acquire wakelock wl1 near the end of bucket #0. + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 2, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + // Release the event at early bucket #1. + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + // Anomaly detected when stopping the alarm. The refractory period does not change. + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Condition changes to false. + screen_on_event = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 20, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + processor->OnLogEvent(screen_on_event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 30, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(acquire_event.get()); + // The condition is false. Do not start the alarm. + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Condition turns true. + screen_off_event = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + processor->OnLogEvent(screen_off_event.get()); + EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + // Condition turns to false. + screen_on_event = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + processor->OnLogEvent(screen_on_event.get()); + // Condition turns to false. Cancelled the alarm. + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + // Detected one anomaly. + EXPECT_EQ(refractory_period_sec + + (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Condition turns to true again. + screen_off_event = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + processor->OnLogEvent(screen_off_event.get()); + EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + release_event = + CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(refractory_period_sec + + (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); +} + +TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) { + const int num_buckets = 3; + const uint64_t threshold_ns = NS_PER_SEC; + auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); + + int64_t bucketStartTimeNs = 10 * NS_PER_SEC; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + auto screen_off_event = CreateScreenStateChangedEvent( + bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + processor->OnLogEvent(screen_off_event.get()); + + // Acquire wakelock "wc1" in bucket #0. + auto acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Release wakelock "wc1" in bucket #0. + auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire wakelock "wc1" in bucket #1. + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 100, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire wakelock "wc2" in bucket #2. + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, + attributionUids3, attributionTags3, "wl2"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2, + anomalyTracker->getAlarmTimestampSec(dimensionKey2)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + // Release wakelock "wc2" in bucket #2. + release_event = + CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC, + attributionUids3, attributionTags3, "wl2"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); + EXPECT_EQ(refractory_period_sec + + (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + // Acquire wakelock "wc1" in bucket #2. + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Release wakelock "wc1" in bucket #2. + release_event = + CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + + (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) / + NS_PER_SEC + + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4, + attributionUids3, attributionTags3, "wl2"); + processor->OnLogEvent(acquire_event.get()); + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey2)); + + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 2, + attributionUids3, attributionTags3, "wl2"); + processor->OnLogEvent(release_event.get()); + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 6, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); + // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered. + EXPECT_EQ(refractory_period_sec + (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); +} + +TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) { + const int num_buckets = 2; + const uint64_t threshold_ns = 3 * NS_PER_SEC; + auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false); + int64_t bucketStartTimeNs = 10 * NS_PER_SEC; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; + + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC; + config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec); + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + auto screen_off_event = CreateScreenStateChangedEvent( + bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + processor->OnLogEvent(screen_off_event.get()); + + // Acquire wakelock "wc1" in bucket #0. + auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 100, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire the wakelock "wc1" again. + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + // The alarm does not change. + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Anomaly alarm fired late. + const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC; + auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( + static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC)); + EXPECT_EQ(1u, alarmSet.size()); + processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + // Within the refractory period. No anomaly. + EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // A new wakelock, but still within refractory period. + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC, + attributionUids1, attributionTags1, "wl1"); + // Still in the refractory period. No anomaly. + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + release_event = + CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp index 605117474014..9e743f7a3157 100644 --- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp @@ -53,367 +53,318 @@ StatsdConfig CreateStatsdConfig(const Position position) { return config; } +// GMS core node is in the middle. +std::vector<int> attributionUids1 = {111, 222, 333}; +std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1", "App3"}; + +// GMS core node is the last one. +std::vector<int> attributionUids2 = {111, 333, 222}; +std::vector<string> attributionTags2 = {"App1", "App3", "GMSCoreModule1"}; + +// GMS core node is the first one. +std::vector<int> attributionUids3 = {222, 333}; +std::vector<string> attributionTags3 = {"GMSCoreModule1", "App3"}; + +// Single GMS core node. +std::vector<int> attributionUids4 = {222}; +std::vector<string> attributionTags4 = {"GMSCoreModule1"}; + +// GMS core has another uid. +std::vector<int> attributionUids5 = {111, 444, 333}; +std::vector<string> attributionTags5 = {"App1", "GMSCoreModule2", "App3"}; + +// Multiple GMS core nodes. +std::vector<int> attributionUids6 = {444, 222}; +std::vector<string> attributionTags6 = {"GMSCoreModule2", "GMSCoreModule1"}; + +// No GMS core nodes +std::vector<int> attributionUids7 = {111, 333}; +std::vector<string> attributionTags7 = {"App1", "App3"}; + +std::vector<int> attributionUids8 = {111}; +std::vector<string> attributionTags8 = {"App1"}; + +// GMS core node with isolated uid. +const int isolatedUid = 666; +std::vector<int> attributionUids9 = {isolatedUid}; +std::vector<string> attributionTags9 = {"GMSCoreModule3"}; + +std::vector<int> attributionUids10 = {isolatedUid}; +std::vector<string> attributionTags10 = {"GMSCoreModule1"}; + } // namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) { -// auto config = CreateStatsdConfig(Position::FIRST); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// // Here it assumes that GMS core has two uids. -// processor->getUidMap()->updateMap( -// 1, {222, 444, 111, 333}, {1, 1, 2, 2}, -// {String16("v1"), String16("v1"), String16("v2"), String16("v2")}, -// {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), -// String16("APP3")}, -// {String16(""), String16(""), String16(""), String16("")}); -// -// // GMS core node is in the middle. -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"), -// CreateAttribution(222, "GMSCoreModule1"), -// CreateAttribution(333, "App3")}; -// -// // GMS core node is the last one. -// std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"), -// CreateAttribution(333, "App3"), -// CreateAttribution(222, "GMSCoreModule1")}; -// -// // GMS core node is the first one. -// std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"), -// CreateAttribution(333, "App3")}; -// -// // Single GMS core node. -// std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")}; -// -// // GMS core has another uid. -// std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"), -// CreateAttribution(444, "GMSCoreModule2"), -// CreateAttribution(333, "App3")}; -// -// // Multiple GMS core nodes. -// std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"), -// CreateAttribution(222, "GMSCoreModule1")}; -// -// // No GMS core nodes. -// std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"), -// CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")}; -// -// // GMS core node with isolated uid. -// const int isolatedUid = 666; -// std::vector<AttributionNodeInternal> attributions9 = { -// CreateAttribution(isolatedUid, "GMSCoreModule3")}; -// -// std::vector<std::unique_ptr<LogEvent>> events; -// // Events 1~4 are in the 1st bucket. -// events.push_back(CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 2)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 200)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions4, "wl1", bucketStartTimeNs + bucketSizeNs)); -// -// // Events 5~8 are in the 3rd bucket. -// events.push_back(CreateAcquireWakelockEvent( -// attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100)); -// events.push_back(CreateIsolatedUidChangedEvent( -// isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1)); -// events.push_back(CreateIsolatedUidChangedEvent( -// isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(countMetrics.data_size(), 4); -// -// auto data = countMetrics.data(0); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111, -// "App1"); -// EXPECT_EQ(data.bucket_info_size(), 2); -// EXPECT_EQ(data.bucket_info(0).count(), 2); -// EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); -// EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); -// EXPECT_EQ(data.bucket_info(1).count(), 1); -// EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); -// EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); -// -// data = countMetrics.data(1); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, -// "GMSCoreModule1"); -// EXPECT_EQ(data.bucket_info_size(), 2); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); -// EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); -// EXPECT_EQ(data.bucket_info(1).count(), 1); -// EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); -// EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); -// -// data = countMetrics.data(2); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, -// "GMSCoreModule3"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); -// EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs); -// -// data = countMetrics.data(3); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 444, -// "GMSCoreModule2"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); -// EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); -//} -// -//TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) { -// auto config = CreateStatsdConfig(Position::ALL); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// // Here it assumes that GMS core has two uids. -// processor->getUidMap()->updateMap( -// 1, {222, 444, 111, 333}, {1, 1, 2, 2}, -// {String16("v1"), String16("v1"), String16("v2"), String16("v2")}, -// {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), -// String16("APP3")}, -// {String16(""), String16(""), String16(""), String16("")}); -// -// // GMS core node is in the middle. -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"), -// CreateAttribution(222, "GMSCoreModule1"), -// CreateAttribution(333, "App3")}; -// -// // GMS core node is the last one. -// std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"), -// CreateAttribution(333, "App3"), -// CreateAttribution(222, "GMSCoreModule1")}; -// -// // GMS core node is the first one. -// std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"), -// CreateAttribution(333, "App3")}; -// -// // Single GMS core node. -// std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")}; -// -// // GMS core has another uid. -// std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"), -// CreateAttribution(444, "GMSCoreModule2"), -// CreateAttribution(333, "App3")}; -// -// // Multiple GMS core nodes. -// std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"), -// CreateAttribution(222, "GMSCoreModule1")}; -// -// // No GMS core nodes. -// std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"), -// CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")}; -// -// // GMS core node with isolated uid. -// const int isolatedUid = 666; -// std::vector<AttributionNodeInternal> attributions9 = { -// CreateAttribution(isolatedUid, "GMSCoreModule1")}; -// -// std::vector<std::unique_ptr<LogEvent>> events; -// // Events 1~4 are in the 1st bucket. -// events.push_back(CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 2)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 200)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions4, "wl1", bucketStartTimeNs + bucketSizeNs)); -// -// // Events 5~8 are in the 3rd bucket. -// events.push_back(CreateAcquireWakelockEvent( -// attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100)); -// events.push_back(CreateIsolatedUidChangedEvent( -// isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1)); -// events.push_back(CreateIsolatedUidChangedEvent( -// isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(countMetrics.data_size(), 6); -// -// auto data = countMetrics.data(0); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(1).count()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, -// data.bucket_info(1).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// ValidateUidDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); -// ValidateUidDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); -// EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); -// -// data = countMetrics.data(2); -// ValidateUidDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2"); -// ValidateUidDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// ValidateUidDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); -// ValidateUidDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); -// ValidateUidDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(bucketStartTimeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(4); -// ValidateUidDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); -// ValidateUidDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); -// ValidateUidDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(bucketStartTimeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(5); -// ValidateUidDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); -// ValidateUidDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2"); -// ValidateUidDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -//} +TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) { + auto config = CreateStatsdConfig(Position::FIRST); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + // Here it assumes that GMS core has two uids. + processor->getUidMap()->updateMap( + 1, {222, 444, 111, 333}, {1, 1, 2, 2}, + {String16("v1"), String16("v1"), String16("v2"), String16("v2")}, + {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), + String16("APP3")}, + {String16(""), String16(""), String16(""), String16("")}); + + std::vector<std::unique_ptr<LogEvent>> events; + // Events 1~4 are in the 1st bucket. + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2, + attributionTags2, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, + attributionUids3, attributionTags3, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4, + attributionTags4, "wl1")); + + // Events 5~8 are in the 3rd bucket. + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, + attributionUids5, attributionTags5, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, + attributionUids6, attributionTags6, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2, + attributionUids7, attributionTags7, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs, + attributionUids8, attributionTags8, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, + attributionUids9, attributionTags9, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100, + attributionUids9, attributionTags9, "wl2")); + events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222, + isolatedUid, true /*is_create*/)); + events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222, + isolatedUid, false /*is_create*/)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(countMetrics.data_size(), 4); + + auto data = countMetrics.data(0); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).count(), 2); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).count(), 1); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); + + data = countMetrics.data(1); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).count(), 1); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + + data = countMetrics.data(2); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule3"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), + bucketStartTimeNs + 3 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs); + + data = countMetrics.data(3); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 444, + "GMSCoreModule2"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); +} + +TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) { + auto config = CreateStatsdConfig(Position::ALL); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + // Here it assumes that GMS core has two uids. + processor->getUidMap()->updateMap( + 1, {222, 444, 111, 333}, {1, 1, 2, 2}, + {String16("v1"), String16("v1"), String16("v2"), String16("v2")}, + {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), + String16("APP3")}, + {String16(""), String16(""), String16(""), String16("")}); + + std::vector<std::unique_ptr<LogEvent>> events; + // Events 1~4 are in the 1st bucket. + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2, + attributionTags2, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, + attributionUids3, attributionTags3, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4, + attributionTags4, "wl1")); + + // Events 5~8 are in the 3rd bucket. + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, + attributionUids5, attributionTags5, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, + attributionUids6, attributionTags6, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2, + attributionUids7, attributionTags7, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs, + attributionUids8, attributionTags8, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, + attributionUids10, attributionTags10, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100, + attributionUids10, attributionTags10, "wl2")); + events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222, + isolatedUid, true /*is_create*/)); + events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222, + isolatedUid, false /*is_create*/)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(countMetrics.data_size(), 6); + + auto data = countMetrics.data(0); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(1).count()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, + data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, + android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + + data = countMetrics.data(2); + ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, + android::util::WAKELOCK_STATE_CHANGED, 444, + "GMSCoreModule2"); + ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, + android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); + ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + ValidateUidDimension(data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, + android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(4); + ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, + android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); + ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, + android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); + ValidateUidDimension(data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(5); + ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, + android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); + ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, + android::util::WAKELOCK_STATE_CHANGED, 444, + "GMSCoreModule2"); + ValidateUidDimension(data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, + android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp index f8edee50a3fd..102bb1ea243d 100644 --- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp @@ -56,54 +56,54 @@ StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) { } // namespace -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(ConfigTtlE2eTest, TestCountMetric) { -// const int num_buckets = 1; -// const int threshold = 3; -// auto config = CreateStatsdConfig(num_buckets, threshold); -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); -// -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// -// FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)111)); -// HashableDimensionKey whatKey1({fieldValue1}); -// MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); -// -// FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)222)); -// HashableDimensionKey whatKey2({fieldValue2}); -// MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); -// -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2); -// processor->OnLogEvent(event.get()); -// -// event = CreateAcquireWakelockEvent(attributions1, "wl2", bucketStartTimeNs + bucketSizeNs + 2); -// processor->OnLogEvent(event.get()); -// -// event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 25 * bucketSizeNs + 2); -// processor->OnLogEvent(event.get()); -// -// EXPECT_EQ((int64_t)(bucketStartTimeNs + 25 * bucketSizeNs + 2 + 2 * 3600 * NS_PER_SEC), -// processor->mMetricsManagers.begin()->second->getTtlEndNs()); -// -// // Clear the data stored on disk as a result of the ttl. -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 25 * bucketSizeNs + 3, false, true, -// ADB_DUMP, FAST, &buffer); -//} - +TEST(ConfigTtlE2eTest, TestCountMetric) { + const int num_buckets = 1; + const int threshold = 3; + auto config = CreateStatsdConfig(num_buckets, threshold); + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); + + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + std::vector<int> attributionUids1 = {111}; + std::vector<string> attributionTags1 = {"App1"}; + + FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)111)); + HashableDimensionKey whatKey1({fieldValue1}); + MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); + + FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)222)); + HashableDimensionKey whatKey2({fieldValue2}); + MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids1, + attributionTags1, "wl2"); + processor->OnLogEvent(event.get()); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * bucketSizeNs + 2, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + + EXPECT_EQ((int64_t)(bucketStartTimeNs + 25 * bucketSizeNs + 2 + 2 * 3600 * NS_PER_SEC), + processor->mMetricsManagers.begin()->second->getTtlEndNs()); + + // Clear the data stored on disk as a result of the ttl. + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 25 * bucketSizeNs + 3, false, true, + ADB_DUMP, FAST, &buffer); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp index a1f74a631f56..2cd7854420a9 100644 --- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp @@ -27,773 +27,775 @@ namespace statsd { #ifdef __ANDROID__ -// TODO(b/149590301): Update these tests to use new socket schema. -///** -// * Test a count metric that has one slice_by_state with no primary fields. -// * -// * Once the CountMetricProducer is initialized, it has one atom id in -// * mSlicedStateAtoms and no entries in mStateGroupMap. -// -// * One StateTracker tracks the state atom, and it has one listener which is the -// * CountMetricProducer that was initialized. -// */ -//TEST(CountMetricE2eTest, TestSlicedState) { -// // Initialize config. -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto syncStartMatcher = CreateSyncStartAtomMatcher(); -// *config.add_atom_matcher() = syncStartMatcher; -// -// auto state = CreateScreenState(); -// *config.add_state() = state; -// -// // Create count metric that slices by screen state. -// int64_t metricId = 123456; -// auto countMetric = config.add_count_metric(); -// countMetric->set_id(metricId); -// countMetric->set_what(syncStartMatcher.id()); -// countMetric->set_bucket(TimeUnit::FIVE_MINUTES); -// countMetric->add_slice_by_state(state.id()); -// -// // Initialize StatsLogProcessor. -// const uint64_t bucketStartTimeNs = 10000000000; // 0:10 -// const uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// -// // Check that CountMetricProducer was initialized correctly. -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); -// EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); -// -// // Check that StateTrackers were initialized correctly. -// EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); -// EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); -// -// /* -// bucket #1 bucket #2 -// | 1 2 3 4 5 6 7 8 9 10 (minutes) -// |-----------------------------|-----------------------------|-- -// x x x x x x (syncStartEvents) -// | | (ScreenIsOnEvent) -// | | (ScreenIsOffEvent) -// | (ScreenUnknownEvent) -// */ -// // Initialize log events - first bucket. -// int appUid = 123; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 50 * NS_PER_SEC)); // 1:00 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 75 * NS_PER_SEC)); // 1:25 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 -// -// // Initialize log events - second bucket. -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 350 * NS_PER_SEC)); // 6:00 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 400 * NS_PER_SEC)); // 6:50 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 450 * NS_PER_SEC)); // 7:40 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 475 * NS_PER_SEC)); // 8:05 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, -// bucketStartTimeNs + 500 * NS_PER_SEC)); // 8:30 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 520 * NS_PER_SEC)); // 8:50 -// -// // Send log events to StatsLogProcessor. -// for (auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// // Check dump report. -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, -// FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); -// EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// // For each CountMetricData, check StateValue info is correct and buckets -// // have correct counts. -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(1, data.bucket_info(1).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(1); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(2); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(2, data.bucket_info(1).count()); -//} -// -///** -// * Test a count metric that has one slice_by_state with a mapping and no -// * primary fields. -// * -// * Once the CountMetricProducer is initialized, it has one atom id in -// * mSlicedStateAtoms and has one entry per state value in mStateGroupMap. -// * -// * One StateTracker tracks the state atom, and it has one listener which is the -// * CountMetricProducer that was initialized. -// */ -//TEST(CountMetricE2eTest, TestSlicedStateWithMap) { -// // Initialize config. -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto syncStartMatcher = CreateSyncStartAtomMatcher(); -// *config.add_atom_matcher() = syncStartMatcher; -// -// auto state = CreateScreenStateWithOnOffMap(); -// *config.add_state() = state; -// -// // Create count metric that slices by screen state with on/off map. -// int64_t metricId = 123456; -// auto countMetric = config.add_count_metric(); -// countMetric->set_id(metricId); -// countMetric->set_what(syncStartMatcher.id()); -// countMetric->set_bucket(TimeUnit::FIVE_MINUTES); -// countMetric->add_slice_by_state(state.id()); -// -// // Initialize StatsLogProcessor. -// const uint64_t bucketStartTimeNs = 10000000000; // 0:10 -// const uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// -// // Check that StateTrackers were initialized correctly. -// EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); -// EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); -// -// // Check that CountMetricProducer was initialized correctly. -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); -// EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); -// -// StateMap map = state.map(); -// for (auto group : map.group()) { -// for (auto value : group.value()) { -// EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value], -// group.group_id()); -// } -// } -// -// /* -// bucket #1 bucket #2 -// | 1 2 3 4 5 6 7 8 9 10 (minutes) -// |-----------------------------|-----------------------------|-- -// x x x x x x x x x (syncStartEvents) -// -----------------------------------------------------------SCREEN_OFF events -// | (ScreenStateUnknownEvent = 0) -// | | (ScreenStateOffEvent = 1) -// | (ScreenStateDozeEvent = 3) -// | (ScreenStateDozeSuspendEvent = 4) -// -----------------------------------------------------------SCREEN_ON events -// | | (ScreenStateOnEvent = 2) -// | (ScreenStateVrEvent = 5) -// | (ScreenStateOnSuspendEvent = 6) -// */ -// // Initialize log events - first bucket. -// int appUid = 123; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; -// -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, -// bucketStartTimeNs + 30 * NS_PER_SEC)); // 0:40 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 120 * NS_PER_SEC)); // 2:10 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_VR, -// bucketStartTimeNs + 180 * NS_PER_SEC)); // 3:10 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, -// bucketStartTimeNs + 210 * NS_PER_SEC)); // 3:40 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 285 * NS_PER_SEC)); // 4:55 -// -// // Initialize log events - second bucket. -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 360 * NS_PER_SEC)); // 6:10 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND, -// bucketStartTimeNs + 390 * NS_PER_SEC)); // 6:40 -// events.push_back(CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND, -// bucketStartTimeNs + 430 * NS_PER_SEC)); // 7:20 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 440 * NS_PER_SEC)); // 7:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 540 * NS_PER_SEC)); // 9:10 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 570 * NS_PER_SEC)); // 9:40 -// -// // Send log events to StatsLogProcessor. -// for (auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// // Check dump report. -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, -// FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); -// EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// // For each CountMetricData, check StateValue info is correct and buckets -// // have correct counts. -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(1); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(4, data.bucket_info(0).count()); -// EXPECT_EQ(2, data.bucket_info(1).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(2); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(1, data.bucket_info(1).count()); -//} -// -///** -// * Test a count metric that has one slice_by_state with a primary field. -// -// * Once the CountMetricProducer is initialized, it should have one -// * MetricStateLink stored. State querying using a non-empty primary key -// * should also work as intended. -// */ -//TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { -// // Initialize config. -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto appCrashMatcher = -// CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED); -// *config.add_atom_matcher() = appCrashMatcher; -// -// auto state = CreateUidProcessState(); -// *config.add_state() = state; -// -// // Create count metric that slices by uid process state. -// int64_t metricId = 123456; -// auto countMetric = config.add_count_metric(); -// countMetric->set_id(metricId); -// countMetric->set_what(appCrashMatcher.id()); -// countMetric->set_bucket(TimeUnit::FIVE_MINUTES); -// countMetric->add_slice_by_state(state.id()); -// MetricStateLink* stateLink = countMetric->add_state_link(); -// stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); -// auto fieldsInWhat = stateLink->mutable_fields_in_what(); -// *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /* uid */}); -// auto fieldsInState = stateLink->mutable_fields_in_state(); -// *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); -// -// // Initialize StatsLogProcessor. -// const uint64_t bucketStartTimeNs = 10000000000; // 0:10 -// const uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// -// // Check that StateTrackers were initialized correctly. -// EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); -// EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); -// -// // Check that CountMetricProducer was initialized correctly. -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID); -// EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); -// EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1); -// -// /* -// NOTE: "1" or "2" represents the uid associated with the state/app crash event -// bucket #1 bucket #2 -// | 1 2 3 4 5 6 7 8 9 10 -// |-----------------------------|-----------------------------|-- -// 1 1 1 1 1 2 1 1 2 (AppCrashEvents) -// -----------------------------------------------------------PROCESS STATE events -// 1 2 (ProcessStateTopEvent = 1002) -// 1 1 (ProcessStateForegroundServiceEvent = 1003) -// 2 (ProcessStateImportantBackgroundEvent = 1006) -// 1 1 1 (ProcessStateImportantForegroundEvent = 1005) -// -// Based on the diagram above, an AppCrashEvent querying for process state value would return: -// - StateTracker::kStateUnknown -// - Important foreground -// - Top -// - Important foreground -// - Foreground service -// - Top (both the app crash and state still have matching uid = 2) -// -// - Foreground service -// - Foreground service -// - Important background -// */ -// // Initialize log events - first bucket. -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back( -// CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 30 * NS_PER_SEC)); // 0:40 -// events.push_back( -// CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, -// bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 120 * NS_PER_SEC)); // 2:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, -// bucketStartTimeNs + 210 * NS_PER_SEC)); // 3:40 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 -// events.push_back(CreateUidProcessStateChangedEvent( -// 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, -// bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 -// events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, -// bucketStartTimeNs + 285 * NS_PER_SEC)); // 4:55 -// -// // Initialize log events - second bucket. -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 360 * NS_PER_SEC)); // 6:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, -// bucketStartTimeNs + 390 * NS_PER_SEC)); // 6:40 -// events.push_back(CreateUidProcessStateChangedEvent( -// 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, -// bucketStartTimeNs + 430 * NS_PER_SEC)); // 7:20 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 440 * NS_PER_SEC)); // 7:30 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 540 * NS_PER_SEC)); // 9:10 -// events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, -// bucketStartTimeNs + 570 * NS_PER_SEC)); // 9:40 -// -// // Send log events to StatsLogProcessor. -// for (auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// // Check dump report. -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, -// FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); -// EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// // For each CountMetricData, check StateValue info is correct and buckets -// // have correct counts. -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(1); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(2); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(2, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(3); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(2, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(4); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(0).value()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(2, data.bucket_info(1).count()); -//} -// -//TEST(CountMetricE2eTest, TestMultipleSlicedStates) { -// // Initialize config. -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto appCrashMatcher = -// CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED); -// *config.add_atom_matcher() = appCrashMatcher; -// -// auto state1 = CreateScreenStateWithOnOffMap(); -// *config.add_state() = state1; -// auto state2 = CreateUidProcessState(); -// *config.add_state() = state2; -// -// // Create count metric that slices by screen state with on/off map and -// // slices by uid process state. -// int64_t metricId = 123456; -// auto countMetric = config.add_count_metric(); -// countMetric->set_id(metricId); -// countMetric->set_what(appCrashMatcher.id()); -// countMetric->set_bucket(TimeUnit::FIVE_MINUTES); -// countMetric->add_slice_by_state(state1.id()); -// countMetric->add_slice_by_state(state2.id()); -// MetricStateLink* stateLink = countMetric->add_state_link(); -// stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); -// auto fieldsInWhat = stateLink->mutable_fields_in_what(); -// *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /* uid */}); -// auto fieldsInState = stateLink->mutable_fields_in_state(); -// *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); -// -// // Initialize StatsLogProcessor. -// const uint64_t bucketStartTimeNs = 10000000000; // 0:10 -// const uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// -// // Check that StateTrackers were properly initialized. -// EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount()); -// EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); -// EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); -// -// // Check that CountMetricProducer was initialized correctly. -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2); -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), UID_PROCESS_STATE_ATOM_ID); -// EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); -// EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1); -// -// StateMap map = state1.map(); -// for (auto group : map.group()) { -// for (auto value : group.value()) { -// EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value], -// group.group_id()); -// } -// } -// -// /* -// bucket #1 bucket #2 -// | 1 2 3 4 5 6 7 8 9 10 (minutes) -// |-----------------------------|-----------------------------|-- -// 1 1 1 1 1 2 1 1 2 (AppCrashEvents) -// -----------------------------------------------------------SCREEN_OFF events -// | (ScreenStateUnknownEvent = 0) -// | | (ScreenStateOffEvent = 1) -// | (ScreenStateDozeEvent = 3) -// -----------------------------------------------------------SCREEN_ON events -// | | (ScreenStateOnEvent = 2) -// | (ScreenStateOnSuspendEvent = 6) -// -----------------------------------------------------------PROCESS STATE events -// 1 2 (ProcessStateTopEvent = 1002) -// 1 (ProcessStateForegroundServiceEvent = 1003) -// 2 (ProcessStateImportantBackgroundEvent = 1006) -// 1 1 1 (ProcessStateImportantForegroundEvent = 1005) -// -// Based on the diagram above, Screen State / Process State pairs for each -// AppCrashEvent are: -// - StateTracker::kStateUnknown / important foreground -// - off / important foreground -// - off / Top -// - on / important foreground -// - off / important foreground -// - off / top -// -// - off / important foreground -// - off / foreground service -// - on / important background -// -// */ -// // Initialize log events - first bucket. -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 5 * NS_PER_SEC)); // 0:15 -// events.push_back( -// CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, -// bucketStartTimeNs + 30 * NS_PER_SEC)); // 0:40 -// events.push_back( -// CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, -// bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 120 * NS_PER_SEC)); // 2:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 160 * NS_PER_SEC)); // 2:50 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, -// bucketStartTimeNs + 210 * NS_PER_SEC)); // 3:40 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 -// events.push_back(CreateUidProcessStateChangedEvent( -// 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, -// bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 -// events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, -// bucketStartTimeNs + 285 * NS_PER_SEC)); // 4:55 -// -// // Initialize log events - second bucket. -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 360 * NS_PER_SEC)); // 6:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, -// bucketStartTimeNs + 380 * NS_PER_SEC)); // 6:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND, -// bucketStartTimeNs + 390 * NS_PER_SEC)); // 6:40 -// events.push_back(CreateUidProcessStateChangedEvent( -// 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, -// bucketStartTimeNs + 420 * NS_PER_SEC)); // 7:10 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 440 * NS_PER_SEC)); // 7:30 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 450 * NS_PER_SEC)); // 7:40 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 520 * NS_PER_SEC)); // 8:50 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 540 * NS_PER_SEC)); // 9:10 -// events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, -// bucketStartTimeNs + 570 * NS_PER_SEC)); // 9:40 -// -// // Send log events to StatsLogProcessor. -// for (auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// // Check dump report. -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, -// FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); -// EXPECT_EQ(6, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// // For each CountMetricData, check StateValue info is correct and buckets -// // have correct counts. -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(1); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(-1, data.slice_by_state(0).value()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(2); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(2, data.bucket_info(0).count()); -// EXPECT_EQ(1, data.bucket_info(1).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(3); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(4); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(5); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(2, data.bucket_info(0).count()); -//} +/** +* Test a count metric that has one slice_by_state with no primary fields. +* +* Once the CountMetricProducer is initialized, it has one atom id in +* mSlicedStateAtoms and no entries in mStateGroupMap. + +* One StateTracker tracks the state atom, and it has one listener which is the +* CountMetricProducer that was initialized. +*/ +TEST(CountMetricE2eTest, TestSlicedState) { + // Initialize config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto syncStartMatcher = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = syncStartMatcher; + + auto state = CreateScreenState(); + *config.add_state() = state; + + // Create count metric that slices by screen state. + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(syncStartMatcher.id()); + countMetric->set_bucket(TimeUnit::FIVE_MINUTES); + countMetric->add_slice_by_state(state.id()); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); + + // Check that StateTrackers were initialized correctly. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + + /* + bucket #1 bucket #2 + | 1 2 3 4 5 6 7 8 9 10 (minutes) + |-----------------------------|-----------------------------|-- + x x x x x x (syncStartEvents) + | | (ScreenIsOnEvent) + | | (ScreenIsOffEvent) + | (ScreenUnknownEvent) + */ + // Initialize log events - first bucket. + std::vector<int> attributionUids1 = {123}; + std::vector<string> attributionTags1 = {"App1"}; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 50 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 1:00 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 75 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 1:25 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 150 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 2:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 200 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 3:30 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 4:20 + + // Initialize log events - second bucket. + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 350 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 6:00 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 6:50 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 450 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 7:40 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 475 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 8:05 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 500 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 8:30 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 520 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 8:50 + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + // Check dump report. + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(1).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, + data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(2, data.bucket_info(1).count()); +} + +/** + * Test a count metric that has one slice_by_state with a mapping and no + * primary fields. + * + * Once the CountMetricProducer is initialized, it has one atom id in + * mSlicedStateAtoms and has one entry per state value in mStateGroupMap. + * + * One StateTracker tracks the state atom, and it has one listener which is the + * CountMetricProducer that was initialized. + */ +TEST(CountMetricE2eTest, TestSlicedStateWithMap) { + // Initialize config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto syncStartMatcher = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = syncStartMatcher; + + auto state = CreateScreenStateWithOnOffMap(); + *config.add_state() = state; + + // Create count metric that slices by screen state with on/off map. + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(syncStartMatcher.id()); + countMetric->set_bucket(TimeUnit::FIVE_MINUTES); + countMetric->add_slice_by_state(state.id()); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // Check that StateTrackers were initialized correctly. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); + + StateMap map = state.map(); + for (auto group : map.group()) { + for (auto value : group.value()) { + EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value], + group.group_id()); + } + } + + /* + bucket #1 bucket #2 + | 1 2 3 4 5 6 7 8 9 10 (minutes) + |-----------------------------|-----------------------------|-- + x x x x x x x x x (syncStartEvents) + -----------------------------------------------------------SCREEN_OFF events + | (ScreenStateUnknownEvent = 0) + | | (ScreenStateOffEvent = 1) + | (ScreenStateDozeEvent = 3) + | (ScreenStateDozeSuspendEvent = + 4) + -----------------------------------------------------------SCREEN_ON events + | | (ScreenStateOnEvent = 2) + | (ScreenStateVrEvent = 5) + | (ScreenStateOnSuspendEvent = 6) + */ + // Initialize log events - first bucket. + std::vector<int> attributionUids1 = {123}; + std::vector<string> attributionTags1 = {"App1"}; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 20 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 0:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 30 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 0:40 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 1:10 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 90 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:40 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 120 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 2:10 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 150 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 180 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_VR)); // 3:10 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 3:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 210 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:40 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 4:20 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 280 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:50 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 285 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 4:55 + + // Initialize log events - second bucket. + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 360 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 6:10 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 390 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 430 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND)); // 7:20 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 440 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 7:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 540 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 9:10 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 570 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 9:40 + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + // Check dump report. + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(4, data.bucket_info(0).count()); + EXPECT_EQ(2, data.bucket_info(1).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(1).count()); +} + +/** +* Test a count metric that has one slice_by_state with a primary field. + +* Once the CountMetricProducer is initialized, it should have one +* MetricStateLink stored. State querying using a non-empty primary key +* should also work as intended. +*/ +TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { + // Initialize config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED); + *config.add_atom_matcher() = appCrashMatcher; + + auto state = CreateUidProcessState(); + *config.add_state() = state; + + // Create count metric that slices by uid process state. + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(appCrashMatcher.id()); + countMetric->set_bucket(TimeUnit::FIVE_MINUTES); + countMetric->add_slice_by_state(state.id()); + MetricStateLink* stateLink = countMetric->add_state_link(); + stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); + auto fieldsInWhat = stateLink->mutable_fields_in_what(); + *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /*uid*/}); + auto fieldsInState = stateLink->mutable_fields_in_state(); + *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/}); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // Check that StateTrackers were initialized correctly. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); + EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1); + + /* + NOTE: "1" or "2" represents the uid associated with the state/app crash event + bucket #1 bucket #2 + | 1 2 3 4 5 6 7 8 9 10 + |------------------------|-------------------------|-- + 1 1 1 1 1 2 1 1 2 (AppCrashEvents) + -----------------------------------------------------PROCESS STATE events + 1 2 (TopEvent = 1002) + 1 1 (ForegroundServiceEvent = 1003) + 2 (ImportantBackgroundEvent = 1006) + 1 1 1 (ImportantForegroundEvent = 1005) + + Based on the diagram above, an AppCrashEvent querying for process state value would return: + - StateTracker::kStateUnknown + - Important foreground + - Top + - Important foreground + - Foreground service + - Top (both the app crash and state still have matching uid = 2) + + - Foreground service + - Foreground service + - Important background + */ + // Initialize log events - first bucket. + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 1:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/)); // 2:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 2:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/)); // 3:30 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 210 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 3:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/)); // 4:20 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 4:50 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/)); // 4:55 + + // Initialize log events - second bucket. + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/)); // 6:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 390 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 6:40 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 430 * NS_PER_SEC, 2 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 7:20 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 440 * NS_PER_SEC, 1 /*uid*/)); // 7:30 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 9:10 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/)); // 9:40 + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + // Check dump report. + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(3); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(4); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(0).value()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(2, data.bucket_info(1).count()); +} + +TEST(CountMetricE2eTest, TestMultipleSlicedStates) { + // Initialize config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED); + *config.add_atom_matcher() = appCrashMatcher; + + auto state1 = CreateScreenStateWithOnOffMap(); + *config.add_state() = state1; + auto state2 = CreateUidProcessState(); + *config.add_state() = state2; + + // Create count metric that slices by screen state with on/off map and + // slices by uid process state. + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(appCrashMatcher.id()); + countMetric->set_bucket(TimeUnit::FIVE_MINUTES); + countMetric->add_slice_by_state(state1.id()); + countMetric->add_slice_by_state(state2.id()); + MetricStateLink* stateLink = countMetric->add_state_link(); + stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); + auto fieldsInWhat = stateLink->mutable_fields_in_what(); + *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /*uid*/}); + auto fieldsInState = stateLink->mutable_fields_in_state(); + *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/}); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // Check that StateTrackers were properly initialized. + EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), UID_PROCESS_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); + EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1); + + StateMap map = state1.map(); + for (auto group : map.group()) { + for (auto value : group.value()) { + EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value], + group.group_id()); + } + } + + /* + bucket #1 bucket #2 + | 1 2 3 4 5 6 7 8 9 10 (minutes) + |------------------------|------------------------|-- + 1 1 1 1 1 2 1 1 2 (AppCrashEvents) + ---------------------------------------------------SCREEN_OFF events + | (ScreenUnknownEvent = 0) + | | (ScreenOffEvent = 1) + | (ScreenDozeEvent = 3) + ---------------------------------------------------SCREEN_ON events + | | (ScreenOnEvent = 2) + | (ScreenOnSuspendEvent = 6) + ---------------------------------------------------PROCESS STATE events + 1 2 (TopEvent = 1002) + 1 (ForegroundServiceEvent = 1003) + 2 (ImportantBackgroundEvent = 1006) + 1 1 1 (ImportantForegroundEvent = 1005) + + Based on the diagram above, Screen State / Process State pairs for each + AppCrashEvent are: + - StateTracker::kStateUnknown / important foreground + - off / important foreground + - off / Top + - on / important foreground + - off / important foreground + - off / top + + - off / important foreground + - off / foreground service + - on / important background + + */ + // Initialize log events - first bucket. + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 5 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:15 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 30 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 0:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 1:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 90 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/)); // 2:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 2:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 160 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:50 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/)); // 3:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 210 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/)); // 4:20 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 4:50 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/)); // 4:55 + + // Initialize log events - second bucket. + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/)); // 6:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 380 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 6:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 390 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 420 * NS_PER_SEC, 2 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 7:10 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 440 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 7:30 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 450 * NS_PER_SEC, 1 /*uid*/)); // 7:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 520 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 8:50 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 9:10 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/)); // 9:40 + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + // Check dump report. + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(6, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(-1, data.slice_by_state(0).value()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(1).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(3); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(4); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(5); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp index 8eb5f69dcf40..b586b06e0175 100644 --- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp @@ -26,688 +26,688 @@ namespace statsd { #ifdef __ANDROID__ -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(DurationMetricE2eTest, TestOneBucket) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); -// *config.add_atom_matcher() = screenOnMatcher; -// *config.add_atom_matcher() = screenOffMatcher; -// -// auto durationPredicate = CreateScreenIsOnPredicate(); -// *config.add_predicate() = durationPredicate; -// -// int64_t metricId = 123456; -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(metricId); -// durationMetric->set_what(durationPredicate.id()); -// durationMetric->set_bucket(FIVE_MINUTES); -// durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// -// const int64_t baseTimeNs = 0; // 0:00 -// const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 -// const int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); -// -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// -// std::unique_ptr<LogEvent> event; -// -// // Screen is off at start of bucket. -// event = CreateScreenStateChangedEvent( -// android::view::DISPLAY_STATE_OFF, configAddedTimeNs); // 0:01 -// processor->OnLogEvent(event.get()); -// -// // Turn screen on. -// const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs); -// processor->OnLogEvent(event.get()); -// -// // Turn off screen 30 seconds after turning on. -// const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs); -// processor->OnLogEvent(event.get()); -// -// event = CreateScreenBrightnessChangedEvent(64, durationEndNs + 1 * NS_PER_SEC); // 0:42 -// processor->OnLogEvent(event.get()); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true, -// ADB_DUMP, FAST, &buffer); // 5:01 -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); -// -// const StatsLogReport::DurationMetricDataWrapper& durationMetrics = -// reports.reports(0).metrics(0).duration_metrics(); -// EXPECT_EQ(1, durationMetrics.data_size()); -// -// auto data = durationMetrics.data(0); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos()); -// EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -//} -// -//TEST(DurationMetricE2eTest, TestTwoBuckets) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); -// *config.add_atom_matcher() = screenOnMatcher; -// *config.add_atom_matcher() = screenOffMatcher; -// -// auto durationPredicate = CreateScreenIsOnPredicate(); -// *config.add_predicate() = durationPredicate; -// -// int64_t metricId = 123456; -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(metricId); -// durationMetric->set_what(durationPredicate.id()); -// durationMetric->set_bucket(FIVE_MINUTES); -// durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// -// const int64_t baseTimeNs = 0; // 0:00 -// const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 -// const int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); -// -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// -// std::unique_ptr<LogEvent> event; -// -// // Screen is off at start of bucket. -// event = CreateScreenStateChangedEvent( -// android::view::DISPLAY_STATE_OFF, configAddedTimeNs); // 0:01 -// processor->OnLogEvent(event.get()); -// -// // Turn screen on. -// const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs); -// processor->OnLogEvent(event.get()); -// -// // Turn off screen 30 seconds after turning on. -// const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs); -// processor->OnLogEvent(event.get()); -// -// event = CreateScreenBrightnessChangedEvent(64, durationEndNs + 1 * NS_PER_SEC); // 0:42 -// processor->OnLogEvent(event.get()); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false, true, -// ADB_DUMP, FAST, &buffer); // 10:01 -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); -// -// const StatsLogReport::DurationMetricDataWrapper& durationMetrics = -// reports.reports(0).metrics(0).duration_metrics(); -// EXPECT_EQ(1, durationMetrics.data_size()); -// -// auto data = durationMetrics.data(0); -// EXPECT_EQ(1, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(0, bucketInfo.bucket_num()); -// EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos()); -// EXPECT_EQ(configAddedTimeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -//} -// -//TEST(DurationMetricE2eTest, TestWithActivation) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); -// auto crashMatcher = CreateProcessCrashAtomMatcher(); -// *config.add_atom_matcher() = screenOnMatcher; -// *config.add_atom_matcher() = screenOffMatcher; -// *config.add_atom_matcher() = crashMatcher; -// -// auto durationPredicate = CreateScreenIsOnPredicate(); -// *config.add_predicate() = durationPredicate; -// -// int64_t metricId = 123456; -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(metricId); -// durationMetric->set_what(durationPredicate.id()); -// durationMetric->set_bucket(FIVE_MINUTES); -// durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// auto metric_activation1 = config.add_metric_activation(); -// metric_activation1->set_metric_id(metricId); -// auto event_activation1 = metric_activation1->add_event_activation(); -// event_activation1->set_atom_matcher_id(crashMatcher.id()); -// event_activation1->set_ttl_seconds(30); // 30 secs. -// -// const int64_t bucketStartTimeNs = 10000000000; -// const int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// sp<UidMap> m = new UidMap(); -// sp<StatsPullerManager> pullerManager = new StatsPullerManager(); -// sp<AlarmMonitor> anomalyAlarmMonitor; -// sp<AlarmMonitor> subscriberAlarmMonitor; -// vector<int64_t> activeConfigsBroadcast; -// -// int broadcastCount = 0; -// StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, -// bucketStartTimeNs, [](const ConfigKey& key) { return true; }, -// [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, -// const vector<int64_t>& activeConfigs) { -// broadcastCount++; -// EXPECT_EQ(broadcastUid, uid); -// activeConfigsBroadcast.clear(); -// activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), -// activeConfigs.begin(), activeConfigs.end()); -// return true; -// }); -// -// processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); // 0:00 -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap.size(), 1u); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// std::unique_ptr<LogEvent> event; -// -// // Turn screen off. -// event = CreateScreenStateChangedEvent( -// android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * NS_PER_SEC); // 0:02 -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC); -// -// // Turn screen on. -// const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs); -// processor.OnLogEvent(event.get(), durationStartNs); -// -// // Activate metric. -// const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10 -// const int64_t activationEndNs = -// activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40 -// event = CreateAppCrashEvent(111, activationStartNs); -// processor.OnLogEvent(event.get(), activationStartNs); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// // Expire activation. -// const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC; -// event = CreateScreenBrightnessChangedEvent(64, expirationNs); // 0:47 -// processor.OnLogEvent(event.get(), expirationNs); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap.size(), 1u); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// // Turn off screen 10 seconds after activation expiration. -// const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs); -// processor.OnLogEvent(event.get(),durationEndNs); -// -// // Turn screen on. -// const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs); -// processor.OnLogEvent(event.get(), duration2StartNs); -// -// // Turn off screen. -// const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, duration2EndNs); -// processor.OnLogEvent(event.get(), duration2EndNs); -// -// // Activate metric. -// const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10 -// const int64_t activation2EndNs = -// activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40 -// event = CreateAppCrashEvent(211, activation2StartNs); -// processor.OnLogEvent(event.get(), activation2StartNs); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, activation2StartNs); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true, -// ADB_DUMP, FAST, &buffer); // 5:01 -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); -// -// const StatsLogReport::DurationMetricDataWrapper& durationMetrics = -// reports.reports(0).metrics(0).duration_metrics(); -// EXPECT_EQ(1, durationMetrics.data_size()); -// -// auto data = durationMetrics.data(0); -// EXPECT_EQ(1, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(0, bucketInfo.bucket_num()); -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(expirationNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(expirationNs - durationStartNs, bucketInfo.duration_nanos()); -//} -// -//TEST(DurationMetricE2eTest, TestWithCondition) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); -// -// auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); -// *config.add_predicate() = holdingWakelockPredicate; -// -// auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); -// *config.add_predicate() = isInBackgroundPredicate; -// -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(StringToId("WakelockDuration")); -// durationMetric->set_what(holdingWakelockPredicate.id()); -// durationMetric->set_condition(isInBackgroundPredicate.id()); -// durationMetric->set_aggregation_type(DurationMetric::SUM); -// durationMetric->set_bucket(FIVE_MINUTES); -// -// ConfigKey cfgKey; -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_TRUE(eventActivationMap.empty()); -// -// int appUid = 123; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; -// -// auto event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10 -// processor->OnLogEvent(event.get()); -// -// event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22 -// processor->OnLogEvent(event.get()); -// -// event = CreateMoveToForegroundEvent( -// appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15 -// processor->OnLogEvent(event.get()); -// -// event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 4 * 60 * NS_PER_SEC); // 4:00 -// processor->OnLogEvent(event.get()); -// -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); -// -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// -// // Validate bucket info. -// EXPECT_EQ(1, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ((2 * 60 + 53) * NS_PER_SEC, bucketInfo.duration_nanos()); -//} -// -//TEST(DurationMetricE2eTest, TestWithSlicedCondition) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); -// -// auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); -// // The predicate is dimensioning by first attribution node by uid. -// FieldMatcher dimensions = CreateAttributionUidDimensions( -// android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); -// *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; -// *config.add_predicate() = holdingWakelockPredicate; -// -// auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); -// *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = -// CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); -// *config.add_predicate() = isInBackgroundPredicate; -// -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(StringToId("WakelockDuration")); -// durationMetric->set_what(holdingWakelockPredicate.id()); -// durationMetric->set_condition(isInBackgroundPredicate.id()); -// durationMetric->set_aggregation_type(DurationMetric::SUM); -// // The metric is dimensioning by first attribution node and only by uid. -// *durationMetric->mutable_dimensions_in_what() = -// CreateAttributionUidDimensions( -// android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); -// durationMetric->set_bucket(FIVE_MINUTES); -// -// // Links between wakelock state atom and condition of app is in background. -// auto links = durationMetric->add_links(); -// links->set_condition(isInBackgroundPredicate.id()); -// auto dimensionWhat = links->mutable_fields_in_what(); -// dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED); -// dimensionWhat->add_child()->set_field(1); // uid field. -// *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( -// android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, { Position::FIRST }); -// -// ConfigKey cfgKey; -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_TRUE(eventActivationMap.empty()); -// -// int appUid = 123; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; -// -// auto event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10 -// processor->OnLogEvent(event.get()); -// -// event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22 -// processor->OnLogEvent(event.get()); -// -// event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 60 * NS_PER_SEC); // 1:00 -// processor->OnLogEvent(event.get()); -// -// -// event = CreateMoveToForegroundEvent( -// appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15 -// processor->OnLogEvent(event.get()); -// -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); -// -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // Validate dimension value. -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, appUid); -// // Validate bucket info. -// EXPECT_EQ(1, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(38 * NS_PER_SEC, bucketInfo.duration_nanos()); -//} -// -//TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); -// *config.add_atom_matcher() = screenOnMatcher; -// -// auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); -// // The predicate is dimensioning by first attribution node by uid. -// FieldMatcher dimensions = CreateAttributionUidDimensions( -// android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); -// *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; -// *config.add_predicate() = holdingWakelockPredicate; -// -// auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); -// *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = -// CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); -// *config.add_predicate() = isInBackgroundPredicate; -// -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(StringToId("WakelockDuration")); -// durationMetric->set_what(holdingWakelockPredicate.id()); -// durationMetric->set_condition(isInBackgroundPredicate.id()); -// durationMetric->set_aggregation_type(DurationMetric::SUM); -// // The metric is dimensioning by first attribution node and only by uid. -// *durationMetric->mutable_dimensions_in_what() = -// CreateAttributionUidDimensions( -// android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); -// durationMetric->set_bucket(FIVE_MINUTES); -// -// // Links between wakelock state atom and condition of app is in background. -// auto links = durationMetric->add_links(); -// links->set_condition(isInBackgroundPredicate.id()); -// auto dimensionWhat = links->mutable_fields_in_what(); -// dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED); -// dimensionWhat->add_child()->set_field(1); // uid field. -// *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( -// android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, { Position::FIRST }); -// -// auto metric_activation1 = config.add_metric_activation(); -// metric_activation1->set_metric_id(durationMetric->id()); -// auto event_activation1 = metric_activation1->add_event_activation(); -// event_activation1->set_atom_matcher_id(screenOnMatcher.id()); -// event_activation1->set_ttl_seconds(60 * 2); // 2 minutes. -// -// ConfigKey cfgKey; -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap.size(), 1u); -// EXPECT_TRUE(eventActivationMap.find(4) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// int appUid = 123; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; -// -// auto event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10 -// processor->OnLogEvent(event.get()); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22 -// processor->OnLogEvent(event.get()); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC; // 0:30 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs); -// processor->OnLogEvent(event.get()); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// const int64_t durationEndNs = -// durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC; // 3:00 -// event = CreateAppCrashEvent(333, durationEndNs); -// processor->OnLogEvent(event.get()); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// event = CreateMoveToForegroundEvent( -// appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15 -// processor->OnLogEvent(event.get()); -// -// event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC); // 4:17 -// processor->OnLogEvent(event.get()); -// -// event = CreateMoveToBackgroundEvent( -// appUid, bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC); // 4:20 -// processor->OnLogEvent(event.get()); -// -// event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC); // 4:25 -// processor->OnLogEvent(event.get()); -// -// const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC; // 4:30 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs); -// processor->OnLogEvent(event.get()); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, duration2StartNs); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); -// -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // Validate dimension value. -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, appUid); -// // Validate bucket info. -// EXPECT_EQ(2, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(durationEndNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos()); -// -// bucketInfo = data.bucket_info(1); -// EXPECT_EQ(durationEndNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos()); -//} +TEST(DurationMetricE2eTest, TestOneBucket) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + *config.add_atom_matcher() = screenOffMatcher; + + auto durationPredicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = durationPredicate; + + int64_t metricId = 123456; + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(metricId); + durationMetric->set_what(durationPredicate.id()); + durationMetric->set_bucket(FIVE_MINUTES); + durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); + + const int64_t baseTimeNs = 0; // 0:00 + const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); + + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + + std::unique_ptr<LogEvent> event; + + // Screen is off at start of bucket. + event = CreateScreenStateChangedEvent(configAddedTimeNs, + android::view::DISPLAY_STATE_OFF); // 0:01 + processor->OnLogEvent(event.get()); + + // Turn screen on. + const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11 + event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(event.get()); + + // Turn off screen 30 seconds after turning on. + const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41 + event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(event.get()); + + event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64); // 0:42 + processor->OnLogEvent(event.get()); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true, + ADB_DUMP, FAST, &buffer); // 5:01 + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + const StatsLogReport::DurationMetricDataWrapper& durationMetrics = + reports.reports(0).metrics(0).duration_metrics(); + EXPECT_EQ(1, durationMetrics.data_size()); + + auto data = durationMetrics.data(0); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos()); + EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); +} + +TEST(DurationMetricE2eTest, TestTwoBuckets) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + *config.add_atom_matcher() = screenOffMatcher; + + auto durationPredicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = durationPredicate; + + int64_t metricId = 123456; + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(metricId); + durationMetric->set_what(durationPredicate.id()); + durationMetric->set_bucket(FIVE_MINUTES); + durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); + + const int64_t baseTimeNs = 0; // 0:00 + const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); + + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + + std::unique_ptr<LogEvent> event; + + // Screen is off at start of bucket. + event = CreateScreenStateChangedEvent(configAddedTimeNs, + android::view::DISPLAY_STATE_OFF); // 0:01 + processor->OnLogEvent(event.get()); + + // Turn screen on. + const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11 + event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(event.get()); + + // Turn off screen 30 seconds after turning on. + const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41 + event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(event.get()); + + event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64); // 0:42 + processor->OnLogEvent(event.get()); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false, + true, ADB_DUMP, FAST, &buffer); // 10:01 + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + const StatsLogReport::DurationMetricDataWrapper& durationMetrics = + reports.reports(0).metrics(0).duration_metrics(); + EXPECT_EQ(1, durationMetrics.data_size()); + + auto data = durationMetrics.data(0); + EXPECT_EQ(1, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(0, bucketInfo.bucket_num()); + EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos()); + EXPECT_EQ(configAddedTimeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); +} + +TEST(DurationMetricE2eTest, TestWithActivation) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); + auto crashMatcher = CreateProcessCrashAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + *config.add_atom_matcher() = screenOffMatcher; + *config.add_atom_matcher() = crashMatcher; + + auto durationPredicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = durationPredicate; + + int64_t metricId = 123456; + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(metricId); + durationMetric->set_what(durationPredicate.id()); + durationMetric->set_bucket(FIVE_MINUTES); + durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); + + auto metric_activation1 = config.add_metric_activation(); + metric_activation1->set_metric_id(metricId); + auto event_activation1 = metric_activation1->add_event_activation(); + event_activation1->set_atom_matcher_id(crashMatcher.id()); + event_activation1->set_ttl_seconds(30); // 30 secs. + + const int64_t bucketStartTimeNs = 10000000000; + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + int broadcastCount = 0; + StatsLogProcessor processor( + m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, + [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), + activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); // 0:00 + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap.size(), 1u); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + std::unique_ptr<LogEvent> event; + + // Turn screen off. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * NS_PER_SEC, + android::view::DISPLAY_STATE_OFF); // 0:02 + processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC); + + // Turn screen on. + const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05 + event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), durationStartNs); + + // Activate metric. + const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10 + const int64_t activationEndNs = + activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40 + event = CreateAppCrashEvent(activationStartNs, 111); + processor.OnLogEvent(event.get(), activationStartNs); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + // Expire activation. + const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC; + event = CreateScreenBrightnessChangedEvent(expirationNs, 64); // 0:47 + processor.OnLogEvent(event.get(), expirationNs); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap.size(), 1u); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + // Turn off screen 10 seconds after activation expiration. + const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50 + event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF); + processor.OnLogEvent(event.get(), durationEndNs); + + // Turn screen on. + const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55 + event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), duration2StartNs); + + // Turn off screen. + const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05 + event = CreateScreenStateChangedEvent(duration2EndNs, android::view::DISPLAY_STATE_OFF); + processor.OnLogEvent(event.get(), duration2EndNs); + + // Activate metric. + const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10 + const int64_t activation2EndNs = + activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40 + event = CreateAppCrashEvent(activation2StartNs, 211); + processor.OnLogEvent(event.get(), activation2StartNs); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, activation2StartNs); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true, + ADB_DUMP, FAST, &buffer); // 5:01 + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + const StatsLogReport::DurationMetricDataWrapper& durationMetrics = + reports.reports(0).metrics(0).duration_metrics(); + EXPECT_EQ(1, durationMetrics.data_size()); + + auto data = durationMetrics.data(0); + EXPECT_EQ(1, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(0, bucketInfo.bucket_num()); + EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(expirationNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(expirationNs - durationStartNs, bucketInfo.duration_nanos()); +} + +TEST(DurationMetricE2eTest, TestWithCondition) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + + auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + *config.add_predicate() = holdingWakelockPredicate; + + auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *config.add_predicate() = isInBackgroundPredicate; + + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_condition(isInBackgroundPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + durationMetric->set_bucket(FIVE_MINUTES); + + ConfigKey cfgKey; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_TRUE(eventActivationMap.empty()); + + int appUid = 123; + vector<int> attributionUids1 = {appUid}; + vector<string> attributionTags1 = {"App1"}; + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1, + attributionTags1, + "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22 + processor->OnLogEvent(event.get()); + + event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, + appUid); // 3:15 + processor->OnLogEvent(event.get()); + + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 4 * 60 * NS_PER_SEC, attributionUids1, + attributionTags1, + "wl1"); // 4:00 + processor->OnLogEvent(event.get()); + + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); + + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + + // Validate bucket info. + EXPECT_EQ(1, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ((2 * 60 + 53) * NS_PER_SEC, bucketInfo.duration_nanos()); +} + +TEST(DurationMetricE2eTest, TestWithSlicedCondition) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + + auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + FieldMatcher dimensions = CreateAttributionUidDimensions(android::util::WAKELOCK_STATE_CHANGED, + {Position::FIRST}); + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; + *config.add_predicate() = holdingWakelockPredicate; + + auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = isInBackgroundPredicate; + + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_condition(isInBackgroundPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( + android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->set_bucket(FIVE_MINUTES); + + // Links between wakelock state atom and condition of app is in background. + auto links = durationMetric->add_links(); + links->set_condition(isInBackgroundPredicate.id()); + auto dimensionWhat = links->mutable_fields_in_what(); + dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED); + dimensionWhat->add_child()->set_field(1); // uid field. + *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( + android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); + + ConfigKey cfgKey; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_TRUE(eventActivationMap.empty()); + + int appUid = 123; + std::vector<int> attributionUids1 = {appUid}; + std::vector<string> attributionTags1 = {"App1"}; + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22 + processor->OnLogEvent(event.get()); + + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 1:00 + processor->OnLogEvent(event.get()); + + event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, + appUid); // 3:15 + processor->OnLogEvent(event.get()); + + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); + + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, appUid); + // Validate bucket info. + EXPECT_EQ(1, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(38 * NS_PER_SEC, bucketInfo.duration_nanos()); +} + +TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + + auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + FieldMatcher dimensions = CreateAttributionUidDimensions(android::util::WAKELOCK_STATE_CHANGED, + {Position::FIRST}); + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; + *config.add_predicate() = holdingWakelockPredicate; + + auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = isInBackgroundPredicate; + + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_condition(isInBackgroundPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( + android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->set_bucket(FIVE_MINUTES); + + // Links between wakelock state atom and condition of app is in background. + auto links = durationMetric->add_links(); + links->set_condition(isInBackgroundPredicate.id()); + auto dimensionWhat = links->mutable_fields_in_what(); + dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED); + dimensionWhat->add_child()->set_field(1); // uid field. + *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( + android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); + + auto metric_activation1 = config.add_metric_activation(); + metric_activation1->set_metric_id(durationMetric->id()); + auto event_activation1 = metric_activation1->add_event_activation(); + event_activation1->set_atom_matcher_id(screenOnMatcher.id()); + event_activation1->set_ttl_seconds(60 * 2); // 2 minutes. + + ConfigKey cfgKey; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap.size(), 1u); + EXPECT_TRUE(eventActivationMap.find(4) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, 0); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + int appUid = 123; + std::vector<int> attributionUids1 = {appUid}; + std::vector<string> attributionTags1 = {"App1"}; + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, 0); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22 + processor->OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, 0); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC; // 0:30 + event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + const int64_t durationEndNs = + durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC; // 3:00 + event = CreateAppCrashEvent(durationEndNs, 333); + processor->OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, + appUid); // 3:15 + processor->OnLogEvent(event.get()); + + event = CreateReleaseWakelockEvent(bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC, + attributionUids1, attributionTags1, "wl1"); // 4:17 + processor->OnLogEvent(event.get()); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC, + appUid); // 4:20 + processor->OnLogEvent(event.get()); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC, + attributionUids1, attributionTags1, "wl1"); // 4:25 + processor->OnLogEvent(event.get()); + + const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC; // 4:30 + event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, duration2StartNs); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); + + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, appUid); + // Validate bucket info. + EXPECT_EQ(2, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(durationEndNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos()); + + bucketInfo = data.bucket_info(1); + EXPECT_EQ(durationEndNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos()); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp index 7f651d4ba529..594c1e6bf6e7 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp @@ -65,482 +65,465 @@ StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type, } // namespaces -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { -// auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// ATOM_TAG); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// -// // When creating the config, the gauge metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& nextPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); -// -// auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 55); -// processor->OnLogEvent(screenOffEvent.get()); -// -// // Pulling alarm arrives on time and reset the sequential pulling alarm. -// processor->informPullAlarmFired(nextPullTimeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); -// -// auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + bucketSizeNs + 10); -// processor->OnLogEvent(screenOnEvent.get()); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + bucketSizeNs + 100); -// processor->OnLogEvent(screenOffEvent.get()); -// -// processor->informPullAlarmFired(nextPullTimeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, -// nextPullTimeNs); -// -// processor->informPullAlarmFired(nextPullTimeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs); -// -// screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 3 * bucketSizeNs + 2); -// processor->OnLogEvent(screenOnEvent.get()); -// -// processor->informPullAlarmFired(nextPullTimeNs + 3); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 5 * bucketSizeNs + 1); -// processor->OnLogEvent(screenOffEvent.get()); -// -// processor->informPullAlarmFired(nextPullTimeNs + 2); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs); -// -// processor->informPullAlarmFired(nextPullTimeNs + 2); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); -// EXPECT_GT((int)gaugeMetrics.data_size(), 1); -// -// auto data = gaugeMetrics.data(0); -// EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// EXPECT_EQ(6, data.bucket_info_size()); -// -// EXPECT_EQ(1, data.bucket_info(0).atom_size()); -// EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(1).atom_size()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, -// data.bucket_info(1).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(2).atom_size()); -// EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, -// data.bucket_info(2).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(3).atom_size()); -// EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1, -// data.bucket_info(3).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(3).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(3).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(4).atom_size()); -// EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, -// data.bucket_info(4).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(4).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(4).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(5).atom_size()); -// EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2, -// data.bucket_info(5).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(5).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(5).atom(0).subsystem_sleep_state().time_millis(), 0); -//} -// -//TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) { -// auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// ATOM_TAG); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// -// auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 55); -// processor->OnLogEvent(screenOffEvent.get()); -// -// auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + bucketSizeNs + 10); -// processor->OnLogEvent(screenOnEvent.get()); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + bucketSizeNs + 100); -// processor->OnLogEvent(screenOffEvent.get()); -// -// screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 3 * bucketSizeNs + 2); -// processor->OnLogEvent(screenOnEvent.get()); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 5 * bucketSizeNs + 1); -// processor->OnLogEvent(screenOffEvent.get()); -// screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 5 * bucketSizeNs + 3); -// processor->OnLogEvent(screenOnEvent.get()); -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 5 * bucketSizeNs + 10); -// processor->OnLogEvent(screenOffEvent.get()); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); -// EXPECT_GT((int)gaugeMetrics.data_size(), 1); -// -// auto data = gaugeMetrics.data(0); -// EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// EXPECT_EQ(3, data.bucket_info_size()); -// -// EXPECT_EQ(1, data.bucket_info(0).atom_size()); -// EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(1).atom_size()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100, -// data.bucket_info(1).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(2, data.bucket_info(2).atom_size()); -// EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, -// data.bucket_info(2).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10, -// data.bucket_info(2).elapsed_timestamp_nanos(1)); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); -// EXPECT_TRUE(data.bucket_info(2).atom(1).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0); -//} -// -// -//TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) { -// auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// ATOM_TAG); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// -// // When creating the config, the gauge metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& nextPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); -// -// auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 55); -// processor->OnLogEvent(screenOffEvent.get()); -// -// auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + bucketSizeNs + 10); -// processor->OnLogEvent(screenOnEvent.get()); -// -// // Pulling alarm arrives one bucket size late. -// processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 3 * bucketSizeNs + 11); -// processor->OnLogEvent(screenOffEvent.get()); -// -// // Pulling alarm arrives more than one bucket size late. -// processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs + 12); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); -// EXPECT_GT((int)gaugeMetrics.data_size(), 1); -// -// auto data = gaugeMetrics.data(0); -// EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// EXPECT_EQ(3, data.bucket_info_size()); -// -// EXPECT_EQ(1, data.bucket_info(0).atom_size()); -// EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(1).atom_size()); -// EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11, -// data.bucket_info(1).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(2).atom_size()); -// EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12, -// data.bucket_info(2).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); -//} -// -//TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) { -// auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false); -// -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; -// -// auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher(); -// *config.add_atom_matcher() = batterySaverStartMatcher; -// const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. -// auto metric_activation = config.add_metric_activation(); -// metric_activation->set_metric_id(metricId); -// metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); -// auto event_activation = metric_activation->add_event_activation(); -// event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); -// event_activation->set_ttl_seconds(ttlNs / 1000000000); -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// ATOM_TAG); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // When creating the config, the gauge metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& nextPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); -// -// // Pulling alarm arrives on time and reset the sequential pulling alarm. -// // Event should not be kept. -// processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // Activate the metric. A pull occurs upon activation. -// const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis. -// auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs); -// processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms. -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // This event should be kept. 2 total. -// processor->informPullAlarmFired(nextPullTimeNs + 1); // 20 mins + 1 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, -// nextPullTimeNs); -// -// // This event should be kept. 3 total. -// processor->informPullAlarmFired(nextPullTimeNs + 2); // 25 mins + 2 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs); -// -// // Create random event to deactivate metric. -// auto deactivationEvent = CreateScreenBrightnessChangedEvent(50, activationNs + ttlNs + 1); -// processor->OnLogEvent(deactivationEvent.get()); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // Event should not be kept. 3 total. -// processor->informPullAlarmFired(nextPullTimeNs + 3); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); -// -// processor->informPullAlarmFired(nextPullTimeNs + 2); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); -// EXPECT_GT((int)gaugeMetrics.data_size(), 0); -// -// auto data = gaugeMetrics.data(0); -// EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// EXPECT_EQ(3, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(1, bucketInfo.atom_size()); -// EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); -// EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0)); -// EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); -// -// bucketInfo = data.bucket_info(1); -// EXPECT_EQ(1, bucketInfo.atom_size()); -// EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0)); -// EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); -// -// bucketInfo = data.bucket_info(2); -// EXPECT_EQ(1, bucketInfo.atom_size()); -// EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0)); -// EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)), -// bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)), -// bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); -//} +TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { + auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = + CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + + // When creating the config, the gauge metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& nextPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); + + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + // Pulling alarm arrives on time and reset the sequential pulling alarm. + processor->informPullAlarmFired(nextPullTimeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); + + auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + processor->informPullAlarmFired(nextPullTimeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); + + processor->informPullAlarmFired(nextPullTimeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs); + + screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + processor->informPullAlarmFired(nextPullTimeNs + 3); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + processor->informPullAlarmFired(nextPullTimeNs + 2); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs); + + processor->informPullAlarmFired(nextPullTimeNs + 2); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); + EXPECT_GT((int)gaugeMetrics.data_size(), 1); + + auto data = gaugeMetrics.data(0); + EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + EXPECT_EQ(6, data.bucket_info_size()); + + EXPECT_EQ(1, data.bucket_info(0).atom_size()); + EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); + EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(1).atom_size()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(2).atom_size()); + EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(3).atom_size()); + EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1, data.bucket_info(3).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(3).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(3).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(4).atom_size()); + EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(4).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(4).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(4).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(5).atom_size()); + EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2, data.bucket_info(5).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(5).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(5).atom(0).subsystem_sleep_state().time_millis(), 0); +} + +TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) { + auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = + CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 3, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 10, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); + EXPECT_GT((int)gaugeMetrics.data_size(), 1); + + auto data = gaugeMetrics.data(0); + EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + EXPECT_EQ(3, data.bucket_info_size()); + + EXPECT_EQ(1, data.bucket_info(0).atom_size()); + EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); + EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(1).atom_size()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100, data.bucket_info(1).elapsed_timestamp_nanos(0)); + EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(2, data.bucket_info(2).atom_size()); + EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10, data.bucket_info(2).elapsed_timestamp_nanos(1)); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); + EXPECT_TRUE(data.bucket_info(2).atom(1).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0); +} + +TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) { + auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = + CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + + // When creating the config, the gauge metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& nextPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); + + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + // Pulling alarm arrives one bucket size late. + processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 11, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + // Pulling alarm arrives more than one bucket size late. + processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs + 12); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); + EXPECT_GT((int)gaugeMetrics.data_size(), 1); + + auto data = gaugeMetrics.data(0); + EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + EXPECT_EQ(3, data.bucket_info_size()); + + EXPECT_EQ(1, data.bucket_info(0).atom_size()); + EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(1).atom_size()); + EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11, + data.bucket_info(1).elapsed_timestamp_nanos(0)); + EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(2).atom_size()); + EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12, data.bucket_info(2).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); +} + +TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) { + auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false); + + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; + + auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher(); + *config.add_atom_matcher() = batterySaverStartMatcher; + const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. + auto metric_activation = config.add_metric_activation(); + metric_activation->set_metric_id(metricId); + metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); + auto event_activation = metric_activation->add_event_activation(); + event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); + event_activation->set_ttl_seconds(ttlNs / 1000000000); + + ConfigKey cfgKey; + auto processor = + CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // When creating the config, the gauge metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& nextPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); + + // Pulling alarm arrives on time and reset the sequential pulling alarm. + // Event should not be kept. + processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // Activate the metric. A pull occurs upon activation. + const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis. + auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs); + processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms. + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // This event should be kept. 2 total. + processor->informPullAlarmFired(nextPullTimeNs + 1); // 20 mins + 1 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); + + // This event should be kept. 3 total. + processor->informPullAlarmFired(nextPullTimeNs + 2); // 25 mins + 2 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs); + + // Create random event to deactivate metric. + auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50); + processor->OnLogEvent(deactivationEvent.get()); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // Event should not be kept. 3 total. + processor->informPullAlarmFired(nextPullTimeNs + 3); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); + + processor->informPullAlarmFired(nextPullTimeNs + 2); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); + EXPECT_GT((int)gaugeMetrics.data_size(), 0); + + auto data = gaugeMetrics.data(0); + EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + EXPECT_EQ(3, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(1, bucketInfo.atom_size()); + EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); + EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0)); + EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); + + bucketInfo = data.bucket_info(1); + EXPECT_EQ(1, bucketInfo.atom_size()); + EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0)); + EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); + + bucketInfo = data.bucket_info(2); + EXPECT_EQ(1, bucketInfo.atom_size()); + EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0)); + EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)), + bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)), + bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); +} TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition) { auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false); diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp index ef6e753b802d..6e3d93a5547f 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp @@ -14,12 +14,13 @@ #include <gtest/gtest.h> +#include <vector> + #include "src/StatsLogProcessor.h" #include "src/stats_log_util.h" +#include "stats_event.h" #include "tests/statsd_test_util.h" -#include <vector> - namespace android { namespace os { namespace statsd { @@ -68,221 +69,227 @@ StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sa return config; } -// TODO(b/149590301): Update this helper to use new socket schema. -//std::unique_ptr<LogEvent> CreateAppStartOccurredEvent( -// const int uid, const string& pkg_name, AppStartOccurred::TransitionType type, -// const string& activity_name, const string& calling_pkg_name, const bool is_instant_app, -// int64_t activity_start_msec, uint64_t timestampNs) { -// auto logEvent = std::make_unique<LogEvent>( -// android::util::APP_START_OCCURRED, timestampNs); -// logEvent->write(uid); -// logEvent->write(pkg_name); -// logEvent->write(type); -// logEvent->write(activity_name); -// logEvent->write(calling_pkg_name); -// logEvent->write(is_instant_app); -// logEvent->write(activity_start_msec); -// logEvent->init(); -// return logEvent; -//} +std::unique_ptr<LogEvent> CreateAppStartOccurredEvent( + uint64_t timestampNs, const int uid, const string& pkg_name, + AppStartOccurred::TransitionType type, const string& activity_name, + const string& calling_pkg_name, const bool is_instant_app, int64_t activity_start_msec) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::APP_START_OCCURRED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, pkg_name.c_str()); + AStatsEvent_writeInt32(statsEvent, type); + AStatsEvent_writeString(statsEvent, activity_name.c_str()); + AStatsEvent_writeString(statsEvent, calling_pkg_name.c_str()); + AStatsEvent_writeInt32(statsEvent, is_instant_app); + AStatsEvent_writeInt32(statsEvent, activity_start_msec); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} } // namespace -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { -// for (const auto& sampling_type : -// { GaugeMetric::FIRST_N_SAMPLES, GaugeMetric:: RANDOM_ONE_SAMPLE }) { -// auto config = CreateStatsdConfigForPushedEvent(sampling_type); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor( -// bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// int appUid1 = 123; -// int appUid2 = 456; -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back(CreateMoveToBackgroundEvent(appUid1, bucketStartTimeNs + 15)); -// events.push_back(CreateMoveToForegroundEvent( -// appUid1, bucketStartTimeNs + bucketSizeNs + 250)); -// events.push_back(CreateMoveToBackgroundEvent( -// appUid1, bucketStartTimeNs + bucketSizeNs + 350)); -// events.push_back(CreateMoveToForegroundEvent( -// appUid1, bucketStartTimeNs + 2 * bucketSizeNs + 100)); -// -// -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::WARM, "activity_name1", "calling_pkg_name1", -// true /*is_instant_app*/, 101 /*activity_start_msec*/, bucketStartTimeNs + 10)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::HOT, "activity_name2", "calling_pkg_name2", -// true /*is_instant_app*/, 102 /*activity_start_msec*/, bucketStartTimeNs + 20)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::COLD, "activity_name3", "calling_pkg_name3", -// true /*is_instant_app*/, 103 /*activity_start_msec*/, bucketStartTimeNs + 30)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::WARM, "activity_name4", "calling_pkg_name4", -// true /*is_instant_app*/, 104 /*activity_start_msec*/, -// bucketStartTimeNs + bucketSizeNs + 30)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::COLD, "activity_name5", "calling_pkg_name5", -// true /*is_instant_app*/, 105 /*activity_start_msec*/, -// bucketStartTimeNs + 2 * bucketSizeNs)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::HOT, "activity_name6", "calling_pkg_name6", -// false /*is_instant_app*/, 106 /*activity_start_msec*/, -// bucketStartTimeNs + 2 * bucketSizeNs + 10)); -// -// events.push_back(CreateMoveToBackgroundEvent( -// appUid2, bucketStartTimeNs + bucketSizeNs + 10)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid2, "app2", AppStartOccurred::COLD, "activity_name7", "calling_pkg_name7", -// true /*is_instant_app*/, 201 /*activity_start_msec*/, -// bucketStartTimeNs + 2 * bucketSizeNs + 10)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); -// EXPECT_EQ(2, gaugeMetrics.data_size()); -// -// auto data = gaugeMetrics.data(0); -// EXPECT_EQ(android::util::APP_START_OCCURRED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(appUid1, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(3, data.bucket_info_size()); -// if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) { -// EXPECT_EQ(2, data.bucket_info(0).atom_size()); -// EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::HOT, -// data.bucket_info(0).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name2", -// data.bucket_info(0).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(102L, -// data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); -// EXPECT_EQ(AppStartOccurred::COLD, -// data.bucket_info(0).atom(1).app_start_occurred().type()); -// EXPECT_EQ("activity_name3", -// data.bucket_info(0).atom(1).app_start_occurred().activity_name()); -// EXPECT_EQ(103L, -// data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis()); -// -// EXPECT_EQ(1, data.bucket_info(1).atom_size()); -// EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::WARM, -// data.bucket_info(1).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name4", -// data.bucket_info(1).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(104L, -// data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); -// -// EXPECT_EQ(2, data.bucket_info(2).atom_size()); -// EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::COLD, -// data.bucket_info(2).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name5", -// data.bucket_info(2).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(105L, -// data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); -// EXPECT_EQ(AppStartOccurred::HOT, -// data.bucket_info(2).atom(1).app_start_occurred().type()); -// EXPECT_EQ("activity_name6", -// data.bucket_info(2).atom(1).app_start_occurred().activity_name()); -// EXPECT_EQ(106L, -// data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis()); -// } else { -// EXPECT_EQ(1, data.bucket_info(0).atom_size()); -// EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::HOT, -// data.bucket_info(0).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name2", -// data.bucket_info(0).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(102L, -// data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); -// -// EXPECT_EQ(1, data.bucket_info(1).atom_size()); -// EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::WARM, -// data.bucket_info(1).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name4", -// data.bucket_info(1).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(104L, -// data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); -// -// EXPECT_EQ(1, data.bucket_info(2).atom_size()); -// EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::COLD, -// data.bucket_info(2).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name5", -// data.bucket_info(2).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(105L, -// data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); -// } -// -// data = gaugeMetrics.data(1); -// -// EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_OCCURRED); -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(appUid2, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).atom_size()); -// EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name7", -// data.bucket_info(0).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); -// } -//} +TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { + for (const auto& sampling_type : + {GaugeMetric::FIRST_N_SAMPLES, GaugeMetric::RANDOM_ONE_SAMPLE}) { + auto config = CreateStatsdConfigForPushedEvent(sampling_type); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + int appUid1 = 123; + int appUid2 = 456; + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid1)); + events.push_back( + CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid1)); + events.push_back( + CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid1)); + events.push_back( + CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, appUid1)); + + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 10, appUid1, "app1", AppStartOccurred::WARM, "activity_name1", + "calling_pkg_name1", true /*is_instant_app*/, 101 /*activity_start_msec*/)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 20, appUid1, "app1", AppStartOccurred::HOT, "activity_name2", + "calling_pkg_name2", true /*is_instant_app*/, 102 /*activity_start_msec*/)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 30, appUid1, "app1", AppStartOccurred::COLD, "activity_name3", + "calling_pkg_name3", true /*is_instant_app*/, 103 /*activity_start_msec*/)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + bucketSizeNs + 30, appUid1, "app1", AppStartOccurred::WARM, + "activity_name4", "calling_pkg_name4", true /*is_instant_app*/, + 104 /*activity_start_msec*/)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 2 * bucketSizeNs, appUid1, "app1", AppStartOccurred::COLD, + "activity_name5", "calling_pkg_name5", true /*is_instant_app*/, + 105 /*activity_start_msec*/)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid1, "app1", AppStartOccurred::HOT, + "activity_name6", "calling_pkg_name6", false /*is_instant_app*/, + 106 /*activity_start_msec*/)); + + events.push_back( + CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 10, appUid2)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid2, "app2", AppStartOccurred::COLD, + "activity_name7", "calling_pkg_name7", true /*is_instant_app*/, + 201 /*activity_start_msec*/)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), + &gaugeMetrics); + EXPECT_EQ(2, gaugeMetrics.data_size()); + + auto data = gaugeMetrics.data(0); + EXPECT_EQ(android::util::APP_START_OCCURRED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(appUid1, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(3, data.bucket_info_size()); + if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) { + EXPECT_EQ(2, data.bucket_info(0).atom_size()); + EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::HOT, + data.bucket_info(0).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name2", + data.bucket_info(0).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(102L, + data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); + EXPECT_EQ(AppStartOccurred::COLD, + data.bucket_info(0).atom(1).app_start_occurred().type()); + EXPECT_EQ("activity_name3", + data.bucket_info(0).atom(1).app_start_occurred().activity_name()); + EXPECT_EQ(103L, + data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis()); + + EXPECT_EQ(1, data.bucket_info(1).atom_size()); + EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::WARM, + data.bucket_info(1).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name4", + data.bucket_info(1).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(104L, + data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); + + EXPECT_EQ(2, data.bucket_info(2).atom_size()); + EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, + data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::COLD, + data.bucket_info(2).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name5", + data.bucket_info(2).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(105L, + data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); + EXPECT_EQ(AppStartOccurred::HOT, + data.bucket_info(2).atom(1).app_start_occurred().type()); + EXPECT_EQ("activity_name6", + data.bucket_info(2).atom(1).app_start_occurred().activity_name()); + EXPECT_EQ(106L, + data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis()); + } else { + EXPECT_EQ(1, data.bucket_info(0).atom_size()); + EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::HOT, + data.bucket_info(0).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name2", + data.bucket_info(0).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(102L, + data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); + + EXPECT_EQ(1, data.bucket_info(1).atom_size()); + EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::WARM, + data.bucket_info(1).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name4", + data.bucket_info(1).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(104L, + data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); + + EXPECT_EQ(1, data.bucket_info(2).atom_size()); + EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, + data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::COLD, + data.bucket_info(2).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name5", + data.bucket_info(2).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(105L, + data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); + } + + data = gaugeMetrics.data(1); + + EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_OCCURRED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(appUid2, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).atom_size()); + EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, + data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name7", + data.bucket_info(0).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); + } +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp index f3f7df775899..1dd90e2b9070 100644 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp @@ -233,1609 +233,1602 @@ StatsdConfig CreateStatsdConfigWithTwoMetricsTwoDeactivations() { } // namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(MetricActivationE2eTest, TestCountMetric) { -// auto config = CreateStatsdConfig(); -// -// int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// sp<UidMap> m = new UidMap(); -// sp<StatsPullerManager> pullerManager = new StatsPullerManager(); -// sp<AlarmMonitor> anomalyAlarmMonitor; -// sp<AlarmMonitor> subscriberAlarmMonitor; -// vector<int64_t> activeConfigsBroadcast; -// -// long timeBase1 = 1; -// int broadcastCount = 0; -// StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, -// bucketStartTimeNs, [](const ConfigKey& key) { return true; }, -// [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, -// const vector<int64_t>& activeConfigs) { -// broadcastCount++; -// EXPECT_EQ(broadcastUid, uid); -// activeConfigsBroadcast.clear(); -// activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), -// activeConfigs.begin(), activeConfigs.end()); -// return true; -// }); -// -// processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // Two activations: one is triggered by battery saver mode (tracker index 0), the other is -// // triggered by screen on event (tracker index 2). -// EXPECT_EQ(eventActivationMap.size(), 2u); -// EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// -// std::unique_ptr<LogEvent> event; -// -// event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 0); -// -// // Activated by battery save mode. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// -// // First processed event. -// event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// -// // Activated by screen on event. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + 20); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// -// // 2nd processed event. -// // The activation by screen_on event expires, but the one by battery save mode is still active. -// event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// // No new broadcast since the config should still be active. -// EXPECT_EQ(broadcastCount, 1); -// -// // 3rd processed event. -// event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// -// // All activations expired. -// event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// -// // Re-activate metric via screen on. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// -// // 4th processed event. -// event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(4, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(4, countMetrics.data_size()); -// -// auto data = countMetrics.data(0); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -//} -// -//TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { -// auto config = CreateStatsdConfigWithOneDeactivation(); -// -// int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// sp<UidMap> m = new UidMap(); -// sp<StatsPullerManager> pullerManager = new StatsPullerManager(); -// sp<AlarmMonitor> anomalyAlarmMonitor; -// sp<AlarmMonitor> subscriberAlarmMonitor; -// vector<int64_t> activeConfigsBroadcast; -// -// long timeBase1 = 1; -// int broadcastCount = 0; -// StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, -// bucketStartTimeNs, [](const ConfigKey& key) { return true; }, -// [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, -// const vector<int64_t>& activeConfigs) { -// broadcastCount++; -// EXPECT_EQ(broadcastUid, uid); -// activeConfigsBroadcast.clear(); -// activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), -// activeConfigs.begin(), activeConfigs.end()); -// return true; -// }); -// -// processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // Two activations: one is triggered by battery saver mode (tracker index 0), the other is -// // triggered by screen on event (tracker index 2). -// EXPECT_EQ(eventActivationMap.size(), 2u); -// EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap.size(), 1u); -// EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); -// EXPECT_EQ(eventDeactivationMap[3].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// std::unique_ptr<LogEvent> event; -// -// event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 0); -// -// // Activated by battery save mode. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // First processed event. -// event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// -// // Activated by screen on event. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + 20); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // 2nd processed event. -// // The activation by screen_on event expires, but the one by battery save mode is still active. -// event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// // No new broadcast since the config should still be active. -// EXPECT_EQ(broadcastCount, 1); -// -// // 3rd processed event. -// event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// -// // All activations expired. -// event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // Re-activate metric via screen on. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // 4th processed event. -// event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // 5th processed event. -// event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// -// // Cancel battery saver mode activation. -// event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // Screen-on activation expired. -// event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 5); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // Cancel battery saver mode activation. -// event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 6); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(5, countMetrics.data_size()); -// -// auto data = countMetrics.data(0); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(4); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -//} -// -//TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { -// auto config = CreateStatsdConfigWithTwoDeactivations(); -// -// int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// sp<UidMap> m = new UidMap(); -// sp<StatsPullerManager> pullerManager = new StatsPullerManager(); -// sp<AlarmMonitor> anomalyAlarmMonitor; -// sp<AlarmMonitor> subscriberAlarmMonitor; -// vector<int64_t> activeConfigsBroadcast; -// -// long timeBase1 = 1; -// int broadcastCount = 0; -// StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, -// bucketStartTimeNs, [](const ConfigKey& key) { return true; }, -// [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, -// const vector<int64_t>& activeConfigs) { -// broadcastCount++; -// EXPECT_EQ(broadcastUid, uid); -// activeConfigsBroadcast.clear(); -// activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), -// activeConfigs.begin(), activeConfigs.end()); -// return true; -// }); -// -// processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // Two activations: one is triggered by battery saver mode (tracker index 0), the other is -// // triggered by screen on event (tracker index 2). -// EXPECT_EQ(eventActivationMap.size(), 2u); -// EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap.size(), 2u); -// EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); -// EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); -// EXPECT_EQ(eventDeactivationMap[3].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[4].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// std::unique_ptr<LogEvent> event; -// -// event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 0); -// -// // Activated by battery save mode. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // First processed event. -// event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// -// // Activated by screen on event. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + 20); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // 2nd processed event. -// // The activation by screen_on event expires, but the one by battery save mode is still active. -// event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// // No new broadcast since the config should still be active. -// EXPECT_EQ(broadcastCount, 1); -// -// // 3rd processed event. -// event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// -// // All activations expired. -// event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // Re-activate metric via screen on. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // 4th processed event. -// event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // 5th processed event. -// event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// -// // Cancel battery saver mode and screen on activation. -// event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // Screen-on activation expired. -// event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 5); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // Cancel battery saver mode and screen on activation. -// event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 6); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(5, countMetrics.data_size()); -// -// auto data = countMetrics.data(0); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(4); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -//} -// -//TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { -// auto config = CreateStatsdConfigWithSameDeactivations(); -// -// int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// sp<UidMap> m = new UidMap(); -// sp<StatsPullerManager> pullerManager = new StatsPullerManager(); -// sp<AlarmMonitor> anomalyAlarmMonitor; -// sp<AlarmMonitor> subscriberAlarmMonitor; -// vector<int64_t> activeConfigsBroadcast; -// -// long timeBase1 = 1; -// int broadcastCount = 0; -// StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, -// bucketStartTimeNs, [](const ConfigKey& key) { return true; }, -// [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, -// const vector<int64_t>& activeConfigs) { -// broadcastCount++; -// EXPECT_EQ(broadcastUid, uid); -// activeConfigsBroadcast.clear(); -// activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), -// activeConfigs.begin(), activeConfigs.end()); -// return true; -// }); -// -// processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // Two activations: one is triggered by battery saver mode (tracker index 0), the other is -// // triggered by screen on event (tracker index 2). -// EXPECT_EQ(eventActivationMap.size(), 2u); -// EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap.size(), 1u); -// EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); -// EXPECT_EQ(eventDeactivationMap[3].size(), 2u); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[3][1], eventActivationMap[2]); -// EXPECT_EQ(broadcastCount, 0); -// -// std::unique_ptr<LogEvent> event; -// -// // Event that should be ignored. -// event = CreateAppCrashEvent(111, bucketStartTimeNs + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 1); -// -// // Activate metric via screen on for 2 minutes. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10); -// -// // 1st processed event. -// event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// -// // Enable battery saver mode activation for 5 minutes. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 + 10); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10); -// -// // 2nd processed event. -// event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 + 40); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40); -// -// // Cancel battery saver mode and screen on activation. -// int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61; -// event = CreateScreenBrightnessChangedEvent(64, firstDeactivation); -// processor.OnLogEvent(event.get(), firstDeactivation); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// -// // Should be ignored -// event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 61 + 80); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// -// // 3rd processed event. -// event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80); -// -// // Cancel battery saver mode activation. -// int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13; -// event = CreateScreenBrightnessChangedEvent(140, secondDeactivation); -// processor.OnLogEvent(event.get(), secondDeactivation); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// -// // Should be ignored. -// event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(3, countMetrics.data_size()); -// -// auto data = countMetrics.data(0); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(555, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(secondDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); -//} -// -//TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { -// auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations(); -// -// int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// sp<UidMap> m = new UidMap(); -// sp<StatsPullerManager> pullerManager = new StatsPullerManager(); -// sp<AlarmMonitor> anomalyAlarmMonitor; -// sp<AlarmMonitor> subscriberAlarmMonitor; -// vector<int64_t> activeConfigsBroadcast; -// -// long timeBase1 = 1; -// int broadcastCount = 0; -// StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, -// bucketStartTimeNs, [](const ConfigKey& key) { return true; }, -// [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, -// const vector<int64_t>& activeConfigs) { -// broadcastCount++; -// EXPECT_EQ(broadcastUid, uid); -// activeConfigsBroadcast.clear(); -// activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), -// activeConfigs.begin(), activeConfigs.end()); -// return true; -// }); -// -// processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 2); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; -// sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1]; -// auto& eventActivationMap2 = metricProducer2->mEventActivationMap; -// auto& eventDeactivationMap2 = metricProducer2->mEventDeactivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_FALSE(metricProducer2->mIsActive); -// // Two activations: one is triggered by battery saver mode (tracker index 0), the other is -// // triggered by screen on event (tracker index 2). -// EXPECT_EQ(eventActivationMap.size(), 2u); -// EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap.size(), 2u); -// EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); -// EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); -// EXPECT_EQ(eventDeactivationMap[3].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[4].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// EXPECT_EQ(eventActivationMap2.size(), 2u); -// EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end()); -// EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end()); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2.size(), 2u); -// EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end()); -// EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end()); -// EXPECT_EQ(eventDeactivationMap[3].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[4].size(), 1u); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// std::unique_ptr<LogEvent> event; -// -// event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); -// event = CreateMoveToForegroundEvent(1111, bucketStartTimeNs + 5); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_FALSE(metricProducer2->mIsActive); -// EXPECT_EQ(broadcastCount, 0); -// -// // Activated by battery save mode. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // First processed event. -// event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// event = CreateMoveToForegroundEvent(2222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// -// // Activated by screen on event. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + 20); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // 2nd processed event. -// // The activation by screen_on event expires, but the one by battery save mode is still active. -// event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// event = CreateMoveToForegroundEvent(3333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// // No new broadcast since the config should still be active. -// EXPECT_EQ(broadcastCount, 1); -// -// // 3rd processed event. -// event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// event = CreateMoveToForegroundEvent(4444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// -// // All activations expired. -// event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// event = CreateMoveToForegroundEvent(5555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// EXPECT_FALSE(metricsManager->isActive()); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_FALSE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // Re-activate metric via screen on. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // 4th processed event. -// event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// event = CreateMoveToForegroundEvent(6666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // 5th processed event. -// event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// event = CreateMoveToForegroundEvent(7777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// -// // Cancel battery saver mode and screen on activation. -// event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// EXPECT_FALSE(metricsManager->isActive()); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_FALSE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // Screen-on activation expired. -// event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// event = CreateMoveToForegroundEvent(8888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_FALSE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// event = CreateMoveToForegroundEvent(9999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 5); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // Cancel battery saver mode and screen on activation. -// event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 6); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_FALSE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(2, reports.reports(0).metrics_size()); -// EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); -// EXPECT_EQ(5, reports.reports(0).metrics(1).count_metrics().data_size()); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(5, countMetrics.data_size()); -// -// auto data = countMetrics.data(0); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(4); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// -// countMetrics.clear_data(); -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(1).count_metrics(), &countMetrics); -// EXPECT_EQ(5, countMetrics.data_size()); -// -// data = countMetrics.data(0); -// EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(2222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(3333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(4444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(6666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(4); -// EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(7777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -//} +TEST(MetricActivationE2eTest, TestCountMetric) { + auto config = CreateStatsdConfig(); + + int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor( + m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, + [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), + activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + + std::unique_ptr<LogEvent> event; + + event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 0); + + // Activated by battery save mode. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + + // First processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + + // Activated by screen on event. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + + // 2nd processed event. + // The activation by screen_on event expires, but the one by battery save mode is still active. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); + + // 3rd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + + // All activations expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + + // Re-activate metric via screen on. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, + android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + + // 4th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(4, reports.reports(0).metrics(0).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(4, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); +} + +TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { + auto config = CreateStatsdConfigWithOneDeactivation(); + + int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor( + m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, + [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), + activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap.size(), 1u); + EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); + EXPECT_EQ(eventDeactivationMap[3].size(), 1u); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + std::unique_ptr<LogEvent> event; + + event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 0); + + // Activated by battery save mode. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // First processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + + // Activated by screen on event. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // 2nd processed event. + // The activation by screen_on event expires, but the one by battery save mode is still active. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); + + // 3rd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + + // All activations expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // Re-activate metric via screen on. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, + android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // 4th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // 5th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); + + // Cancel battery saver mode activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // Screen-on activation expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 5); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // Cancel battery saver mode activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 6); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(5, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(4); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13, + data.bucket_info(0).end_bucket_elapsed_nanos()); +} + +TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { + auto config = CreateStatsdConfigWithTwoDeactivations(); + + int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor( + m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, + [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), + activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap.size(), 2u); + EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); + EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); + EXPECT_EQ(eventDeactivationMap[3].size(), 1u); + EXPECT_EQ(eventDeactivationMap[4].size(), 1u); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + std::unique_ptr<LogEvent> event; + + event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 0); + + // Activated by battery save mode. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // First processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + + // Activated by screen on event. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // 2nd processed event. + // The activation by screen_on event expires, but the one by battery save mode is still active. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); + + // 3rd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + + // All activations expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // Re-activate metric via screen on. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, + android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // 4th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // 5th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); + + // Cancel battery saver mode and screen on activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // Screen-on activation expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 5); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // Cancel battery saver mode and screen on activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 6); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(5, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(4); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); +} + +TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { + auto config = CreateStatsdConfigWithSameDeactivations(); + + int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor( + m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, + [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), + activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap.size(), 1u); + EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); + EXPECT_EQ(eventDeactivationMap[3].size(), 2u); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[3][1], eventActivationMap[2]); + EXPECT_EQ(broadcastCount, 0); + + std::unique_ptr<LogEvent> event; + + // Event that should be ignored. + event = CreateAppCrashEvent(bucketStartTimeNs + 1, 111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 1); + + // Activate metric via screen on for 2 minutes. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10); + + // 1st processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + + // Enable battery saver mode activation for 5 minutes. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 + 10); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10); + + // 2nd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 40, 333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40); + + // Cancel battery saver mode and screen on activation. + int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61; + event = CreateScreenBrightnessChangedEvent(firstDeactivation, 64); + processor.OnLogEvent(event.get(), firstDeactivation); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + + // Should be ignored + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 61 + 80, 444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + + // 3rd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80, 555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80); + + // Cancel battery saver mode activation. + int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13; + event = CreateScreenBrightnessChangedEvent(secondDeactivation, 140); + processor.OnLogEvent(event.get(), secondDeactivation); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + + // Should be ignored. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80, 666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(3, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(555, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(secondDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); +} + +TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { + auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations(); + + int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor( + m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, + [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), + activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 2); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; + sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1]; + auto& eventActivationMap2 = metricProducer2->mEventActivationMap; + auto& eventDeactivationMap2 = metricProducer2->mEventDeactivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_FALSE(metricProducer2->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap.size(), 2u); + EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); + EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); + EXPECT_EQ(eventDeactivationMap[3].size(), 1u); + EXPECT_EQ(eventDeactivationMap[4].size(), 1u); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + EXPECT_EQ(eventActivationMap2.size(), 2u); + EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end()); + EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end()); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2.size(), 2u); + EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end()); + EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end()); + EXPECT_EQ(eventDeactivationMap[3].size(), 1u); + EXPECT_EQ(eventDeactivationMap[4].size(), 1u); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + std::unique_ptr<LogEvent> event; + + event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + 5, 1111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_FALSE(metricProducer2->mIsActive); + EXPECT_EQ(broadcastCount, 0); + + // Activated by battery save mode. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // First processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + 15, 2222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + + // Activated by screen on event. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // 2nd processed event. + // The activation by screen_on event expires, but the one by battery save mode is still active. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 3333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); + + // 3rd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 4444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + + // All activations expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 5555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); + EXPECT_FALSE(metricsManager->isActive()); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_FALSE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // Re-activate metric via screen on. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, + android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // 4th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 6666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // 5th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 7777); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); + + // Cancel battery saver mode and screen on activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); + EXPECT_FALSE(metricsManager->isActive()); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_FALSE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // Screen-on activation expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 8888); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_FALSE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 9999); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 5); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // Cancel battery saver mode and screen on activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 6); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_FALSE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(2, reports.reports(0).metrics_size()); + EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); + EXPECT_EQ(5, reports.reports(0).metrics(1).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(5, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(4); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + countMetrics.clear_data(); + sortMetricDataByDimensionsValue(reports.reports(0).metrics(1).count_metrics(), &countMetrics); + EXPECT_EQ(5, countMetrics.data_size()); + + data = countMetrics.data(0); + EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(2222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(3333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(4444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(6666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(4); + EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(7777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp index 7d93fcced0ac..e8fb523deeb2 100644 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp @@ -97,250 +97,247 @@ StatsdConfig CreateStatsdConfig() { } } // namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests, -//// we should use the real API which will clear the data after dump data is called. -//TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) { -// auto config = CreateStatsdConfig(); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// int appUid = 123; -// auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1); -// auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201); -// auto crashEvent3= CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101); -// -// auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51); -// auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299); -// auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001); -// -// auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16); -// auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249); -// -// auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351); -// auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2); -// -// auto screenTurnedOnEvent = -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 2); -// auto screenTurnedOffEvent = -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 200); -// auto screenTurnedOnEvent2 = -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 2 * bucketSizeNs - 100); -// -// std::vector<AttributionNodeInternal> attributions = { -// CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")}; -// auto syncOnEvent1 = -// CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50); -// auto syncOffEvent1 = -// CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); -// auto syncOnEvent2 = -// CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); -// -// auto moveToBackgroundEvent1 = -// CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15); -// auto moveToForegroundEvent1 = -// CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250); -// -// auto moveToBackgroundEvent2 = -// CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350); -// auto moveToForegroundEvent2 = -// CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1); -// -// /* -// bucket #1 bucket #2 -// -// -// | | | | | | | | | | (crashEvents) -// |-------------------------------------|-----------------------------------|--------- -// -// | | (MoveToBkground) -// -// | | (MoveToForeground) -// -// | | (SyncIsOn) -// | (SyncIsOff) -// | | (ScreenIsOn) -// | (ScreenIsOff) -// */ -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back(std::move(crashEvent1)); -// events.push_back(std::move(crashEvent2)); -// events.push_back(std::move(crashEvent3)); -// events.push_back(std::move(crashEvent4)); -// events.push_back(std::move(crashEvent5)); -// events.push_back(std::move(crashEvent6)); -// events.push_back(std::move(crashEvent7)); -// events.push_back(std::move(crashEvent8)); -// events.push_back(std::move(crashEvent9)); -// events.push_back(std::move(crashEvent10)); -// events.push_back(std::move(screenTurnedOnEvent)); -// events.push_back(std::move(screenTurnedOffEvent)); -// events.push_back(std::move(screenTurnedOnEvent2)); -// events.push_back(std::move(syncOnEvent1)); -// events.push_back(std::move(syncOffEvent1)); -// events.push_back(std::move(syncOnEvent2)); -// events.push_back(std::move(moveToBackgroundEvent1)); -// events.push_back(std::move(moveToForegroundEvent1)); -// events.push_back(std::move(moveToBackgroundEvent2)); -// events.push_back(std::move(moveToForegroundEvent2)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// // Validate dimension value. -// EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); -// // Uid field. -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); -//} -// -//TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) { -// auto config = CreateStatsdConfig(); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor( -// bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// int appUid = 123; -// auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1); -// auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201); -// auto crashEvent3 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101); -// -// auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51); -// auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299); -// auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001); -// -// auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16); -// auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249); -// -// auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351); -// auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2); -// -// auto screenTurnedOnEvent = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2); -// auto screenTurnedOffEvent = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200); -// auto screenTurnedOnEvent2 = -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 2 * bucketSizeNs - 100); -// -// std::vector<AttributionNodeInternal> attributions = { -// CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")}; -// auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50); -// auto syncOffEvent1 = -// CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); -// auto syncOnEvent2 = -// CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); -// -// auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15); -// auto moveToForegroundEvent1 = -// CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250); -// -// auto moveToBackgroundEvent2 = -// CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350); -// auto moveToForegroundEvent2 = -// CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1); -// -// /* -// bucket #1 bucket #2 -// -// -// | | | | | | | | | | (crashEvents) -// |-------------------------------------|-----------------------------------|--------- -// -// | | (MoveToBkground) -// -// | | (MoveToForeground) -// -// | | (SyncIsOn) -// | (SyncIsOff) -// | | (ScreenIsOn) -// | (ScreenIsOff) -// */ -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back(std::move(crashEvent1)); -// events.push_back(std::move(crashEvent2)); -// events.push_back(std::move(crashEvent3)); -// events.push_back(std::move(crashEvent4)); -// events.push_back(std::move(crashEvent5)); -// events.push_back(std::move(crashEvent6)); -// events.push_back(std::move(crashEvent7)); -// events.push_back(std::move(crashEvent8)); -// events.push_back(std::move(crashEvent9)); -// events.push_back(std::move(crashEvent10)); -// events.push_back(std::move(screenTurnedOnEvent)); -// events.push_back(std::move(screenTurnedOffEvent)); -// events.push_back(std::move(screenTurnedOnEvent2)); -// events.push_back(std::move(syncOnEvent1)); -// events.push_back(std::move(syncOffEvent1)); -// events.push_back(std::move(syncOnEvent2)); -// events.push_back(std::move(moveToBackgroundEvent1)); -// events.push_back(std::move(moveToForegroundEvent1)); -// events.push_back(std::move(moveToBackgroundEvent2)); -// events.push_back(std::move(moveToForegroundEvent2)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3); -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// // Validate dimension value. -// EXPECT_EQ(data.dimensions_in_what().field(), -// android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); -// // Uid field. -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); -//} +// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests, +// we should use the real API which will clear the data after dump data is called. +TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) { + auto config = CreateStatsdConfig(); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + int appUid = 123; + auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid); + auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid); + auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid); + + auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid); + auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid); + auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid); + + auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid); + auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid); + + auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid); + auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid); + + auto screenTurnedOnEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + auto screenTurnedOffEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + + std::vector<int> attributionUids = {appUid, appUid + 1}; + std::vector<string> attributionTags = {"App1", "GMSCoreModule1"}; + + auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids, + attributionTags, "ReadEmail"); + auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids, + attributionTags, "ReadEmail"); + auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000, + attributionUids, attributionTags, "ReadDoc"); + + auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid); + auto moveToForegroundEvent1 = + CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid); + + auto moveToBackgroundEvent2 = + CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid); + auto moveToForegroundEvent2 = + CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid); + + /* + bucket #1 bucket #2 + + + | | | | | | | | | | (crashEvents) + |-------------------------------------|-----------------------------------|--------- + + | | (MoveToBkground) + + | | (MoveToForeground) + + | | (SyncIsOn) + | (SyncIsOff) + | | (ScreenIsOn) + | (ScreenIsOff) + */ + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(std::move(crashEvent1)); + events.push_back(std::move(crashEvent2)); + events.push_back(std::move(crashEvent3)); + events.push_back(std::move(crashEvent4)); + events.push_back(std::move(crashEvent5)); + events.push_back(std::move(crashEvent6)); + events.push_back(std::move(crashEvent7)); + events.push_back(std::move(crashEvent8)); + events.push_back(std::move(crashEvent9)); + events.push_back(std::move(crashEvent10)); + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(syncOnEvent1)); + events.push_back(std::move(syncOffEvent1)); + events.push_back(std::move(syncOnEvent2)); + events.push_back(std::move(moveToBackgroundEvent1)); + events.push_back(std::move(moveToForegroundEvent1)); + events.push_back(std::move(moveToBackgroundEvent2)); + events.push_back(std::move(moveToForegroundEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + // Validate dimension value. + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + // Uid field. + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); +} + +TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) { + auto config = CreateStatsdConfig(); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + int appUid = 123; + auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid); + auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid); + auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid); + + auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid); + auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid); + auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid); + + auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid); + auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid); + + auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid); + auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid); + + auto screenTurnedOnEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + auto screenTurnedOffEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + + std::vector<int> attributionUids = {appUid, appUid + 1}; + std::vector<string> attributionTags = {"App1", "GMSCoreModule1"}; + + auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids, + attributionTags, "ReadEmail"); + auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids, + attributionTags, "ReadEmail"); + auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000, + attributionUids, attributionTags, "ReadDoc"); + + auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid); + auto moveToForegroundEvent1 = + CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid); + + auto moveToBackgroundEvent2 = + CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid); + auto moveToForegroundEvent2 = + CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid); + + /* + bucket #1 bucket #2 + + + | | | | | | | | | | (crashEvents) + |-------------------------------------|-----------------------------------|--------- + + | | (MoveToBkground) + + | | (MoveToForeground) + + | | (SyncIsOn) + | (SyncIsOff) + | | (ScreenIsOn) + | (ScreenIsOff) + */ + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(std::move(crashEvent1)); + events.push_back(std::move(crashEvent2)); + events.push_back(std::move(crashEvent3)); + events.push_back(std::move(crashEvent4)); + events.push_back(std::move(crashEvent5)); + events.push_back(std::move(crashEvent6)); + events.push_back(std::move(crashEvent7)); + events.push_back(std::move(crashEvent8)); + events.push_back(std::move(crashEvent9)); + events.push_back(std::move(crashEvent10)); + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(syncOnEvent1)); + events.push_back(std::move(syncOffEvent1)); + events.push_back(std::move(syncOnEvent2)); + events.push_back(std::move(moveToBackgroundEvent1)); + events.push_back(std::move(moveToForegroundEvent1)); + events.push_back(std::move(moveToBackgroundEvent2)); + events.push_back(std::move(moveToForegroundEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3); + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + // Validate dimension value. + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + // Uid field. + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp index 9ec831b567bf..b97590761785 100644 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp @@ -113,96 +113,107 @@ StatsdConfig MakeGaugeMetricConfig(int64_t minTime) { } } // anonymous namespace -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { -// shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); -// SendConfig(service, MakeConfig()); -// int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are -// // initialized with. -// -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get()); -// -// ConfigMetricsReport report = GetReports(service->mProcessor, start + 3); -// // Expect no metrics since the bucket has not finished yet. -// EXPECT_EQ(1, report.metrics_size()); -// EXPECT_EQ(0, report.metrics(0).count_metrics().data_size()); -//} -// -//TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { -// shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); -// SendConfig(service, MakeConfig()); -// int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are -// // initialized with. -// -// // Force the uidmap to update at timestamp 2. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); -// // This is a new installation, so there shouldn't be a split (should be same as the without -// // split case). -// service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), -// String16("")); -// // Goes into the second bucket. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get()); -// -// ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); -// EXPECT_EQ(1, report.metrics_size()); -// EXPECT_EQ(0, report.metrics(0).count_metrics().data_size()); -//} -// -//TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { -// shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); -// SendConfig(service, MakeConfig()); -// int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are -// // initialized with. -// service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, -// {String16("")}); -// -// // Force the uidmap to update at timestamp 2. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); -// service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), -// String16("")); -// // Goes into the second bucket. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get()); -// -// ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); -// backfillStartEndTimestamp(&report); -// -// ASSERT_EQ(1, report.metrics_size()); -// ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); -// ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); -// EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0). -// has_start_bucket_elapsed_nanos()); -// EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0). -// has_end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); -//} -// -//TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { -// shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); -// SendConfig(service, MakeConfig()); -// int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are -// // initialized with. -// service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, -// {String16("")}); -// -// // Force the uidmap to update at timestamp 2. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); -// service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1); -// // Goes into the second bucket. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get()); -// -// ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); -// backfillStartEndTimestamp(&report); -// -// ASSERT_EQ(1, report.metrics_size()); -// ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); -// ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); -// EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0). -// has_start_bucket_elapsed_nanos()); -// EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0). -// has_end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); -//} +TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + SendConfig(service, MakeConfig()); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 2, 100).get()); + + ConfigMetricsReport report = GetReports(service->mProcessor, start + 3); + // Expect no metrics since the bucket has not finished yet. + EXPECT_EQ(1, report.metrics_size()); + EXPECT_EQ(0, report.metrics(0).count_metrics().data_size()); +} + +TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + SendConfig(service, MakeConfig()); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + + // Force the uidmap to update at timestamp 2. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); + // This is a new installation, so there shouldn't be a split (should be same as the without + // split case). + service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), + String16("")); + // Goes into the second bucket. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); + + ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); + EXPECT_EQ(1, report.metrics_size()); + EXPECT_EQ(0, report.metrics(0).count_metrics().data_size()); +} + +TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + SendConfig(service, MakeConfig()); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, + {String16("")}); + + // Force the uidmap to update at timestamp 2. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); + service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), + String16("")); + // Goes into the second bucket. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); + + ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); + backfillStartEndTimestamp(&report); + + ASSERT_EQ(1, report.metrics_size()); + ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); + ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); + EXPECT_TRUE(report.metrics(0) + .count_metrics() + .data(0) + .bucket_info(0) + .has_start_bucket_elapsed_nanos()); + EXPECT_TRUE(report.metrics(0) + .count_metrics() + .data(0) + .bucket_info(0) + .has_end_bucket_elapsed_nanos()); + EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); +} + +TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + SendConfig(service, MakeConfig()); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, + {String16("")}); + + // Force the uidmap to update at timestamp 2. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); + service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1); + // Goes into the second bucket. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); + + ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); + backfillStartEndTimestamp(&report); + + ASSERT_EQ(1, report.metrics_size()); + ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); + ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); + EXPECT_TRUE(report.metrics(0) + .count_metrics() + .data(0) + .bucket_info(0) + .has_start_bucket_elapsed_nanos()); + EXPECT_TRUE(report.metrics(0) + .count_metrics() + .data(0) + .bucket_info(0) + .has_end_bucket_elapsed_nanos()); + EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); +} TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp index 99dbaf17c85c..a87bb71b12bc 100644 --- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp @@ -64,317 +64,313 @@ StatsdConfig CreateStatsdConfig(bool useCondition = true) { } // namespace -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(ValueMetricE2eTest, TestPulledEvents) { -// auto config = CreateStatsdConfig(); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// android::util::SUBSYSTEM_SLEEP_STATE); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// -// // When creating the config, the value metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& expectedPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); -// -// auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 55); -// processor->OnLogEvent(screenOffEvent.get()); -// -// auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 65); -// processor->OnLogEvent(screenOnEvent.get()); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 75); -// processor->OnLogEvent(screenOffEvent.get()); -// -// // Pulling alarm arrives on time and reset the sequential pulling alarm. -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// -// screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 2 * bucketSizeNs + 15); -// processor->OnLogEvent(screenOnEvent.get()); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 4 * bucketSizeNs + 11); -// processor->OnLogEvent(screenOffEvent.get()); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::ValueMetricDataWrapper valueMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).value_metrics(), &valueMetrics); -// EXPECT_GT((int)valueMetrics.data_size(), 1); -// -// auto data = valueMetrics.data(0); -// EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// // We have 4 buckets, the first one was incomplete since the condition was unknown. -// EXPECT_EQ(4, data.bucket_info_size()); -// -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(0).values_size()); -// -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(1).values_size()); -// -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(2).values_size()); -// -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(3).values_size()); -//} -// -//TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) { -// auto config = CreateStatsdConfig(); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// // 10 mins == 2 bucket durations. -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// android::util::SUBSYSTEM_SLEEP_STATE); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// -// // When creating the config, the value metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& expectedPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); -// -// // Screen off/on/off events. -// auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 55); -// processor->OnLogEvent(screenOffEvent.get()); -// -// auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 65); -// processor->OnLogEvent(screenOnEvent.get()); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 75); -// processor->OnLogEvent(screenOffEvent.get()); -// -// // Pulling alarm arrives late by 2 buckets and 1 ns. 2 buckets late is too far away in the -// // future, data will be skipped. -// processor->informPullAlarmFired(expectedPullTimeNs + 2 * bucketSizeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs); -// -// // This screen state change will start a new bucket. -// screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 4 * bucketSizeNs + 65); -// processor->OnLogEvent(screenOnEvent.get()); -// -// // The alarm is delayed but we already created a bucket thanks to the screen state condition. -// // This bucket does not have to be skipped since the alarm arrives in time for the next bucket. -// processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 6 * bucketSizeNs + 31); -// processor->OnLogEvent(screenOffEvent.get()); -// -// processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 8 * bucketSizeNs, expectedPullTimeNs); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 9 * bucketSizeNs, expectedPullTimeNs); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::ValueMetricDataWrapper valueMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).value_metrics(), &valueMetrics); -// EXPECT_GT((int)valueMetrics.data_size(), 1); -// -// auto data = valueMetrics.data(0); -// EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// EXPECT_EQ(3, data.bucket_info_size()); -// -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(0).values_size()); -// -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(1).values_size()); -// -// EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(2).values_size()); -//} -// -//TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { -// auto config = CreateStatsdConfig(false); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; -// -// auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher(); -// *config.add_atom_matcher() = batterySaverStartMatcher; -// const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. -// auto metric_activation = config.add_metric_activation(); -// metric_activation->set_metric_id(metricId); -// metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); -// auto event_activation = metric_activation->add_event_activation(); -// event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); -// event_activation->set_ttl_seconds(ttlNs / 1000000000); -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// android::util::SUBSYSTEM_SLEEP_STATE); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // When creating the config, the value metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& expectedPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); -// -// // Pulling alarm arrives on time and reset the sequential pulling alarm. -// processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // Activate the metric. A pull occurs here -// const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis. -// auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs); -// processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms. -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs); -// -// // Create random event to deactivate metric. -// auto deactivationEvent = CreateScreenBrightnessChangedEvent(50, activationNs + ttlNs + 1); -// processor->OnLogEvent(deactivationEvent.get()); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 3); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, expectedPullTimeNs); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 4); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::ValueMetricDataWrapper valueMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).value_metrics(), &valueMetrics); -// EXPECT_GT((int)valueMetrics.data_size(), 0); -// -// auto data = valueMetrics.data(0); -// EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// // We have 2 full buckets, the two surrounding the activation are dropped. -// EXPECT_EQ(2, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, bucketInfo.values_size()); -// -// bucketInfo = data.bucket_info(1); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, bucketInfo.values_size()); -//} +TEST(ValueMetricE2eTest, TestPulledEvents) { + auto config = CreateStatsdConfig(); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), + android::util::SUBSYSTEM_SLEEP_STATE); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + + // When creating the config, the value metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& expectedPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); + + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + auto screenOnEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + // Pulling alarm arrives on time and reset the sequential pulling alarm. + processor->informPullAlarmFired(expectedPullTimeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + + screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 2 * bucketSizeNs + 15, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 11, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::ValueMetricDataWrapper valueMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); + EXPECT_GT((int)valueMetrics.data_size(), 1); + + auto data = valueMetrics.data(0); + EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + // We have 4 buckets, the first one was incomplete since the condition was unknown. + EXPECT_EQ(4, data.bucket_info_size()); + + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(0).values_size()); + + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(1).values_size()); + + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(2).values_size()); + + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(3).values_size()); +} + +TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) { + auto config = CreateStatsdConfig(); + int64_t baseTimeNs = getElapsedRealtimeNs(); + // 10 mins == 2 bucket durations. + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), + android::util::SUBSYSTEM_SLEEP_STATE); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + + // When creating the config, the value metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& expectedPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); + + // Screen off/on/off events. + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + auto screenOnEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + // Pulling alarm arrives late by 2 buckets and 1 ns. 2 buckets late is too far away in the + // future, data will be skipped. + processor->informPullAlarmFired(expectedPullTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs); + + // This screen state change will start a new bucket. + screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 65, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + // The alarm is delayed but we already created a bucket thanks to the screen state condition. + // This bucket does not have to be skipped since the alarm arrives in time for the next bucket. + processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 6 * bucketSizeNs + 31, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 8 * bucketSizeNs, expectedPullTimeNs); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 9 * bucketSizeNs, expectedPullTimeNs); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::ValueMetricDataWrapper valueMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); + EXPECT_GT((int)valueMetrics.data_size(), 1); + + auto data = valueMetrics.data(0); + EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + EXPECT_EQ(3, data.bucket_info_size()); + + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(0).values_size()); + + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(1).values_size()); + + EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(2).values_size()); +} + +TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { + auto config = CreateStatsdConfig(false); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; + + auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher(); + *config.add_atom_matcher() = batterySaverStartMatcher; + const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. + auto metric_activation = config.add_metric_activation(); + metric_activation->set_metric_id(metricId); + metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); + auto event_activation = metric_activation->add_event_activation(); + event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); + event_activation->set_ttl_seconds(ttlNs / 1000000000); + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), + android::util::SUBSYSTEM_SLEEP_STATE); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // When creating the config, the value metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& expectedPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); + + // Pulling alarm arrives on time and reset the sequential pulling alarm. + processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // Activate the metric. A pull occurs here + const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis. + auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs); + processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms. + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs); + + processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs); + + // Create random event to deactivate metric. + auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50); + processor->OnLogEvent(deactivationEvent.get()); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + processor->informPullAlarmFired(expectedPullTimeNs + 3); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, expectedPullTimeNs); + + processor->informPullAlarmFired(expectedPullTimeNs + 4); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::ValueMetricDataWrapper valueMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); + EXPECT_GT((int)valueMetrics.data_size(), 0); + + auto data = valueMetrics.data(0); + EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + // We have 2 full buckets, the two surrounding the activation are dropped. + EXPECT_EQ(2, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(1, bucketInfo.values_size()); + + bucketInfo = data.bucket_info(1); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(1, bucketInfo.values_size()); +} /** * Test initialization of a simple value metric that is sliced by a state. diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp index 21092e2260d9..ddd8f956737e 100644 --- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp @@ -61,292 +61,290 @@ StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) return config; } -std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"), - CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; - -std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"), - CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; - -// TODO(b/149590301): Update this helper to use new socket schema. -///* -//Events: -//Screen off is met from (200ns,1 min+500ns]. -//Acquire event for wl1 from 2ns to 1min+2ns -//Acquire event for wl2 from 1min-10ns to 2min-15ns -//*/ -//void FeedEvents(StatsdConfig config, sp<StatsLogProcessor> processor) { -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// -// auto screenTurnedOnEvent = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1); -// auto screenTurnedOffEvent = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200); -// auto screenTurnedOnEvent2 = -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + bucketSizeNs + 500); -// -// auto acquireEvent1 = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2); -// auto releaseEvent1 = -// CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2); -// auto acquireEvent2 = -// CreateAcquireWakelockEvent(attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10); -// auto releaseEvent2 = CreateReleaseWakelockEvent(attributions2, "wl2", -// bucketStartTimeNs + 2 * bucketSizeNs - 15); -// -// std::vector<std::unique_ptr<LogEvent>> events; -// -// events.push_back(std::move(screenTurnedOnEvent)); -// events.push_back(std::move(screenTurnedOffEvent)); -// events.push_back(std::move(screenTurnedOnEvent2)); -// events.push_back(std::move(acquireEvent1)); -// events.push_back(std::move(acquireEvent2)); -// events.push_back(std::move(releaseEvent1)); -// events.push_back(std::move(releaseEvent2)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -//} +std::vector<int> attributionUids1 = {111, 222, 222}; +std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1", "GMSCoreModule2"}; + +std::vector<int> attributionUids2 = {111, 222, 222}; +std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1", "GMSCoreModule2"}; + +/* +Events: +Screen off is met from (200ns,1 min+500ns]. +Acquire event for wl1 from 2ns to 1min+2ns +Acquire event for wl2 from 1min-10ns to 2min-15ns +*/ +void FeedEvents(StatsdConfig config, sp<StatsLogProcessor> processor) { + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + + auto screenTurnedOnEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + auto screenTurnedOffEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 500, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + + auto acquireEvent1 = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1"); + auto releaseEvent1 = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, + attributionUids1, attributionTags1, "wl1"); + auto acquireEvent2 = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 10, + attributionUids2, attributionTags2, "wl2"); + auto releaseEvent2 = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 15, + attributionUids2, attributionTags2, "wl2"); + + std::vector<std::unique_ptr<LogEvent>> events; + + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(acquireEvent1)); + events.push_back(std::move(acquireEvent2)); + events.push_back(std::move(releaseEvent1)); + events.push_back(std::move(releaseEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } +} } // namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::SUM); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// // Only 1 dimension output. The tag dimension in the predicate has been aggregated. -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); -// -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // Validate dimension value. -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, 111); -// // Validate bucket info. -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); -// data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // The wakelock holding interval starts from the screen off event and to the end of the 1st -// // bucket. -// EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200); -//} -// -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::SUM); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); -// // Dump the report after the end of 2nd bucket. -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // Validate dimension value. -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, 111); -// // Two output buckets. -// // The wakelock holding interval in the 1st bucket starts from the screen off event and to -// // the end of the 1st bucket. -// EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), -// bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200)); -// // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and -// // ends at the second screen on event. -// EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL); -//} -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::SUM); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 2 * bucketSizeNs + 90)); -// events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3", -// bucketStartTimeNs + 2 * bucketSizeNs + 100)); -// events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3", -// bucketStartTimeNs + 5 * bucketSizeNs + 100)); -// sortLogEventsByTimestamp(&events); -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6); -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, 111); -// // The last wakelock holding spans 4 buckets. -// EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100); -// EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs); -// EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs); -// EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL); -//} -// -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(reports.reports_size(), 1); -// -// // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as -// // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by -// // itself. -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size()); -//} -// -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); -// // Dump the report after the end of 2nd bucket. One dimension with one bucket. -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // Validate dimension value. -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, 111); -// // The max is acquire event for wl1 to screen off start. -// EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200); -//} -// -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 2 * bucketSizeNs + 90)); -// events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3", -// bucketStartTimeNs + 2 * bucketSizeNs + 100)); -// events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3", -// bucketStartTimeNs + 5 * bucketSizeNs + 100)); -// sortLogEventsByTimestamp(&events); -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, 111); -// // The last wakelock holding spans 4 buckets. -// EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs); -// EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(), -// bucketStartTimeNs + 5 * bucketSizeNs); -// EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_elapsed_nanos(), -// bucketStartTimeNs + 6 * bucketSizeNs); -//} +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + // Only 1 dimension output. The tag dimension in the predicate has been aggregated. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // Validate bucket info. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); + data = reports.reports(0).metrics(0).duration_metrics().data(0); + // The wakelock holding interval starts from the screen off event and to the end of the 1st + // bucket. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + // Dump the report after the end of 2nd bucket. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // Two output buckets. + // The wakelock holding interval in the 1st bucket starts from the screen off event and to + // the end of the 1st bucket. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), + bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200)); + // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and + // ends at the second screen on event. + EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back( + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, + attributionUids1, attributionTags1, "wl3")); + events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100, + attributionUids1, attributionTags1, "wl3")); + sortLogEventsByTimestamp(&events); + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // The last wakelock holding spans 4 buckets. + EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100); + EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(reports.reports_size(), 1); + + // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as + // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by + // itself. + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size()); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + // Dump the report after the end of 2nd bucket. One dimension with one bucket. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // The max is acquire event for wl1 to screen off start. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back( + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, + attributionUids1, attributionTags1, "wl3")); + events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100, + attributionUids1, attributionTags1, "wl3")); + sortLogEventsByTimestamp(&events); + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // The last wakelock holding spans 4 buckets. + EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(), + bucketStartTimeNs + 5 * bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 6 * bucketSizeNs); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp index c0b4f436530f..e8200d5c7f52 100644 --- a/cmds/statsd/tests/external/StatsPuller_test.cpp +++ b/cmds/statsd/tests/external/StatsPuller_test.cpp @@ -15,11 +15,14 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <stdio.h> + #include <chrono> #include <thread> #include <vector> + #include "../metrics/metrics_test_helper.h" #include "src/stats_log_util.h" +#include "stats_event.h" #include "tests/statsd_test_util.h" #ifdef __ANDROID__ @@ -57,13 +60,22 @@ private: FakePuller puller; -// TODO(b/149590301): Update this helper to use new socket schema. -//shared_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) { -// shared_ptr<LogEvent> event = make_shared<LogEvent>(pullTagId, eventTimeNs); -// event->write(value); -// event->init(); -// return event; -//} +std::unique_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, pullTagId); + AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); + + AStatsEvent_writeInt64(statsEvent, value); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} class StatsPullerTest : public ::testing::Test { public: @@ -80,149 +92,148 @@ public: } // Anonymous namespace. -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST_F(StatsPullerTest, PullSuccess) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = true; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_TRUE(puller.Pull(&dataHolder)); -// EXPECT_EQ(1, dataHolder.size()); -// EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); -// EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); -// EXPECT_EQ(1, dataHolder[0]->size()); -// EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -// -// sleep_for(std::chrono::seconds(1)); -// -// pullData.clear(); -// pullData.push_back(createSimpleEvent(2222L, 44)); -// -// pullSuccess = true; -// -// EXPECT_TRUE(puller.Pull(&dataHolder)); -// EXPECT_EQ(1, dataHolder.size()); -// EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); -// EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs()); -// EXPECT_EQ(1, dataHolder[0]->size()); -// EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value); -//} -// -//TEST_F(StatsPullerTest, PullFailAfterSuccess) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = true; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_TRUE(puller.Pull(&dataHolder)); -// EXPECT_EQ(1, dataHolder.size()); -// EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); -// EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); -// EXPECT_EQ(1, dataHolder[0]->size()); -// EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -// -// sleep_for(std::chrono::seconds(1)); -// -// pullData.clear(); -// pullData.push_back(createSimpleEvent(2222L, 44)); -// -// pullSuccess = false; -// dataHolder.clear(); -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -// -// pullSuccess = true; -// dataHolder.clear(); -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -//} -// -//// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown -//TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// pullSuccess = true; -// // timeout is 0.5 -// pullDelayNs = (long)(0.8 * NS_PER_SEC); -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -// -// pullData.clear(); -// pullData.push_back(createSimpleEvent(2222L, 44)); -// -// pullSuccess = true; -// dataHolder.clear(); -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -//} -// -//TEST_F(StatsPullerTest, PullFail) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = false; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -//} -// -//TEST_F(StatsPullerTest, PullTakeTooLong) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = true; -// pullDelayNs = NS_PER_SEC; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -//} -// -//TEST_F(StatsPullerTest, PullTooFast) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = true; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_TRUE(puller.Pull(&dataHolder)); -// EXPECT_EQ(1, dataHolder.size()); -// EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); -// EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); -// EXPECT_EQ(1, dataHolder[0]->size()); -// EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -// -// pullData.clear(); -// pullData.push_back(createSimpleEvent(2222L, 44)); -// -// pullSuccess = true; -// -// dataHolder.clear(); -// EXPECT_TRUE(puller.Pull(&dataHolder)); -// EXPECT_EQ(1, dataHolder.size()); -// EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); -// EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); -// EXPECT_EQ(1, dataHolder[0]->size()); -// EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -//} -// -//TEST_F(StatsPullerTest, PullFailsAndTooFast) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = false; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -// -// pullData.clear(); -// pullData.push_back(createSimpleEvent(2222L, 44)); -// -// pullSuccess = true; -// -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -//} +TEST_F(StatsPullerTest, PullSuccess) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = true; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_TRUE(puller.Pull(&dataHolder)); + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); + + sleep_for(std::chrono::seconds(1)); + + pullData.clear(); + pullData.push_back(createSimpleEvent(2222L, 44)); + + pullSuccess = true; + + EXPECT_TRUE(puller.Pull(&dataHolder)); + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value); +} + +TEST_F(StatsPullerTest, PullFailAfterSuccess) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = true; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_TRUE(puller.Pull(&dataHolder)); + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); + + sleep_for(std::chrono::seconds(1)); + + pullData.clear(); + pullData.push_back(createSimpleEvent(2222L, 44)); + + pullSuccess = false; + dataHolder.clear(); + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); + + pullSuccess = true; + dataHolder.clear(); + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} + +// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown +TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) { + pullData.push_back(createSimpleEvent(1111L, 33)); + pullSuccess = true; + // timeout is 0.5 + pullDelayNs = (long)(0.8 * NS_PER_SEC); + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); + + pullData.clear(); + pullData.push_back(createSimpleEvent(2222L, 44)); + + pullSuccess = true; + dataHolder.clear(); + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} + +TEST_F(StatsPullerTest, PullFail) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = false; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} + +TEST_F(StatsPullerTest, PullTakeTooLong) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = true; + pullDelayNs = NS_PER_SEC; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} + +TEST_F(StatsPullerTest, PullTooFast) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = true; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_TRUE(puller.Pull(&dataHolder)); + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); + + pullData.clear(); + pullData.push_back(createSimpleEvent(2222L, 44)); + + pullSuccess = true; + + dataHolder.clear(); + EXPECT_TRUE(puller.Pull(&dataHolder)); + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); +} + +TEST_F(StatsPullerTest, PullFailsAndTooFast) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = false; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); + + pullData.clear(); + pullData.push_back(createSimpleEvent(2222L, 44)); + + pullSuccess = true; + + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp index 81590a2d1e43..f21954f20d08 100644 --- a/cmds/statsd/tests/external/puller_util_test.cpp +++ b/cmds/statsd/tests/external/puller_util_test.cpp @@ -13,12 +13,16 @@ // limitations under the License. #include "external/puller_util.h" + #include <gmock/gmock.h> #include <gtest/gtest.h> #include <stdio.h> + #include <vector> -#include "statslog.h" + #include "../metrics/metrics_test_helper.h" +#include "stats_event.h" +#include "statslog.h" #ifdef __ANDROID__ @@ -58,212 +62,187 @@ void extractIntoVector(vector<shared_ptr<LogEvent>> events, ret.push_back(vec); } } + +std::shared_ptr<LogEvent> makeUidLogEvent(uint64_t timestampNs, int uid, int data1, int data2) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, uidAtomTagId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeInt32(statsEvent, data1); + AStatsEvent_writeInt32(statsEvent, data2); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::shared_ptr<LogEvent> makeNonUidAtomLogEvent(uint64_t timestampNs, int data1) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, nonUidAtomTagId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, data1); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + } // anonymous namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(puller_util, MergeNoDimension) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// // 30->22->31 -// event->write(isolatedUid); -// event->write(hostNonAdditiveData); -// event->write(isolatedAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 20->22->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(hostNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) -// .WillRepeatedly(Return(hostUid)); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) -// .WillRepeatedly(ReturnArg<0>()); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); -// -// vector<vector<int>> actual; -// extractIntoVector(inputData, actual); -// vector<int> expectedV1 = {20, 22, 52}; -// EXPECT_EQ(1, (int)actual.size()); -// EXPECT_THAT(actual, Contains(expectedV1)); -//} -// -//TEST(puller_util, MergeWithDimension) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// // 30->32->31 -// event->write(isolatedUid); -// event->write(isolatedNonAdditiveData); -// event->write(isolatedAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 20->32->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(isolatedNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 20->22->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(hostNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) -// .WillRepeatedly(Return(hostUid)); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) -// .WillRepeatedly(ReturnArg<0>()); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); -// -// vector<vector<int>> actual; -// extractIntoVector(inputData, actual); -// vector<int> expectedV1 = {20, 22, 21}; -// vector<int> expectedV2 = {20, 32, 52}; -// EXPECT_EQ(2, (int)actual.size()); -// EXPECT_THAT(actual, Contains(expectedV1)); -// EXPECT_THAT(actual, Contains(expectedV2)); -//} -// -//TEST(puller_util, NoMergeHostUidOnly) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// // 20->32->31 -// event->write(hostUid); -// event->write(isolatedNonAdditiveData); -// event->write(isolatedAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 20->22->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(hostNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) -// .WillRepeatedly(Return(hostUid)); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) -// .WillRepeatedly(ReturnArg<0>()); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); -// -// // 20->32->31 -// // 20->22->21 -// vector<vector<int>> actual; -// extractIntoVector(inputData, actual); -// vector<int> expectedV1 = {20, 32, 31}; -// vector<int> expectedV2 = {20, 22, 21}; -// EXPECT_EQ(2, (int)actual.size()); -// EXPECT_THAT(actual, Contains(expectedV1)); -// EXPECT_THAT(actual, Contains(expectedV2)); -//} -// -//TEST(puller_util, IsolatedUidOnly) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// // 30->32->31 -// event->write(hostUid); -// event->write(isolatedNonAdditiveData); -// event->write(isolatedAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 30->22->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(hostNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) -// .WillRepeatedly(Return(hostUid)); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) -// .WillRepeatedly(ReturnArg<0>()); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); -// -// // 20->32->31 -// // 20->22->21 -// vector<vector<int>> actual; -// extractIntoVector(inputData, actual); -// vector<int> expectedV1 = {20, 32, 31}; -// vector<int> expectedV2 = {20, 22, 21}; -// EXPECT_EQ(2, (int)actual.size()); -// EXPECT_THAT(actual, Contains(expectedV1)); -// EXPECT_THAT(actual, Contains(expectedV2)); -//} -// -//TEST(puller_util, MultipleIsolatedUidToOneHostUid) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// // 30->32->31 -// event->write(isolatedUid); -// event->write(isolatedNonAdditiveData); -// event->write(isolatedAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 31->32->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(isolatedUid + 1); -// event->write(isolatedNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 20->32->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(isolatedNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid)); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); -// -// vector<vector<int>> actual; -// extractIntoVector(inputData, actual); -// vector<int> expectedV1 = {20, 32, 73}; -// EXPECT_EQ(1, (int)actual.size()); -// EXPECT_THAT(actual, Contains(expectedV1)); -//} -// -//TEST(puller_util, NoNeedToMerge) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = -// make_shared<LogEvent>(nonUidAtomTagId, timestamp); -// // 32 -// event->write(isolatedNonAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// event = make_shared<LogEvent>(nonUidAtomTagId, timestamp); -// // 22 -// event->write(hostNonAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId, {} /*no additive fields*/); -// -// EXPECT_EQ(2, (int)inputData.size()); -//} +TEST(puller_util, MergeNoDimension) { + vector<shared_ptr<LogEvent>> inputData; + + // 30->22->31 + inputData.push_back( + makeUidLogEvent(timestamp, isolatedUid, hostNonAdditiveData, isolatedAdditiveData)); + + // 20->22->21 + inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + + vector<vector<int>> actual; + extractIntoVector(inputData, actual); + vector<int> expectedV1 = {20, 22, 52}; + EXPECT_EQ(1, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); +} + +TEST(puller_util, MergeWithDimension) { + vector<shared_ptr<LogEvent>> inputData; + + // 30->32->31 + inputData.push_back( + makeUidLogEvent(timestamp, isolatedUid, isolatedNonAdditiveData, isolatedAdditiveData)); + + // 20->32->21 + inputData.push_back( + makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, hostAdditiveData)); + + // 20->22->21 + inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + + vector<vector<int>> actual; + extractIntoVector(inputData, actual); + vector<int> expectedV1 = {20, 22, 21}; + vector<int> expectedV2 = {20, 32, 52}; + EXPECT_EQ(2, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); + EXPECT_THAT(actual, Contains(expectedV2)); +} + +TEST(puller_util, NoMergeHostUidOnly) { + vector<shared_ptr<LogEvent>> inputData; + + // 20->32->31 + inputData.push_back( + makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, isolatedAdditiveData)); + + // 20->22->21 + inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + + // 20->32->31 + // 20->22->21 + vector<vector<int>> actual; + extractIntoVector(inputData, actual); + vector<int> expectedV1 = {20, 32, 31}; + vector<int> expectedV2 = {20, 22, 21}; + EXPECT_EQ(2, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); + EXPECT_THAT(actual, Contains(expectedV2)); +} + +TEST(puller_util, IsolatedUidOnly) { + vector<shared_ptr<LogEvent>> inputData; + + // 30->32->31 + inputData.push_back( + makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, isolatedAdditiveData)); + + // 30->22->21 + inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + + // 20->32->31 + // 20->22->21 + vector<vector<int>> actual; + extractIntoVector(inputData, actual); + vector<int> expectedV1 = {20, 32, 31}; + vector<int> expectedV2 = {20, 22, 21}; + EXPECT_EQ(2, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); + EXPECT_THAT(actual, Contains(expectedV2)); +} + +TEST(puller_util, MultipleIsolatedUidToOneHostUid) { + vector<shared_ptr<LogEvent>> inputData; + + // 30->32->31 + inputData.push_back( + makeUidLogEvent(timestamp, isolatedUid, isolatedNonAdditiveData, isolatedAdditiveData)); + + // 31->32->21 + inputData.push_back( + makeUidLogEvent(timestamp, isolatedUid + 1, isolatedNonAdditiveData, hostAdditiveData)); + + // 20->32->21 + inputData.push_back( + makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, hostAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid)); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + + vector<vector<int>> actual; + extractIntoVector(inputData, actual); + vector<int> expectedV1 = {20, 32, 73}; + EXPECT_EQ(1, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); +} + +TEST(puller_util, NoNeedToMerge) { + vector<shared_ptr<LogEvent>> inputData; + + // 32 + inputData.push_back(makeNonUidAtomLogEvent(timestamp, isolatedNonAdditiveData)); + + // 22 + inputData.push_back(makeNonUidAtomLogEvent(timestamp, hostNonAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId, {} /*no additive fields*/); + + EXPECT_EQ(2, (int)inputData.size()); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp index c4407f48d978..6dc041f9fb6e 100644 --- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp +++ b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp @@ -42,7 +42,7 @@ std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) { size_t size; uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/-1, /*pid=*/-1); + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); logEvent->parseBuffer(buf, size); AStatsEvent_release(statsEvent); return logEvent; diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index b8826780c7b9..d55996cb1b7a 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -13,16 +13,19 @@ // limitations under the License. #include "src/metrics/CountMetricProducer.h" -#include "src/stats_log_util.h" -#include "metrics_test_helper.h" -#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> #include <math.h> #include <stdio.h> + #include <vector> +#include "metrics_test_helper.h" +#include "src/stats_log_util.h" +#include "stats_event.h" +#include "tests/statsd_test_util.h" + using namespace testing; using android::sp; using std::set; @@ -37,366 +40,392 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(CountMetricProducerTest, TestFirstBucket) { -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5, -// 600 * NS_PER_SEC + NS_PER_SEC / 2); -// EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(10, countProducer.mCurrentBucketNum); -// EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs()); -//} -// -//TEST(CountMetricProducerTest, TestNonDimensionalEvents) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; -// int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; -// int tagId = 1; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.init(); -// LogEvent event2(tagId, bucketStartTimeNs + 2); -// event2.init(); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// bucketStartTimeNs, bucketStartTimeNs); -// -// // 2 events in bucket 1. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); -// -// // Flushes at event #2. -// countProducer.flushIfNeededLocked(bucketStartTimeNs + 2); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// -// // Flushes. -// countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); -// EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// countProducer.mPastBuckets.end()); -// const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets.size()); -// EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(2LL, buckets[0].mCount); -// -// // 1 matched event happens in bucket 2. -// LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2); -// event3.init(); -// -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); -// countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); -// EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// countProducer.mPastBuckets.end()); -// EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1]; -// EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs); -// EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs); -// EXPECT_EQ(1LL, bucketInfo2.mCount); -// -// // nothing happens in bucket 3. we should not record anything for bucket 3. -// countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); -// EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// countProducer.mPastBuckets.end()); -// const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(2UL, buckets3.size()); -//} -// -//TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_condition(StringToId("SCREEN_ON")); -// -// LogEvent event1(1, bucketStartTimeNs + 1); -// event1.init(); -// -// LogEvent event2(1, bucketStartTimeNs + 10); -// event2.init(); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, -// bucketStartTimeNs); -// -// countProducer.onConditionChanged(true, bucketStartTimeNs); -// countProducer.onMatchedLogEvent(1 /*matcher index*/, event1); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// -// countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2); -// // Upon this match event, the matched event1 is flushed. -// countProducer.onMatchedLogEvent(1 /*matcher index*/, event2); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// -// countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); -// EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// countProducer.mPastBuckets.end()); -// { -// const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets.size()); -// const auto& bucketInfo = buckets[0]; -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); -// EXPECT_EQ(1LL, bucketInfo.mCount); -// } -//} -// -//TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// -// int tagId = 1; -// int conditionTagId = 2; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); -// MetricConditionLink* link = metric.add_links(); -// link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); -// buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what()); -// buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition()); -// -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.write("111"); // uid -// event1.init(); -// ConditionKey key1; -// key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = -// {getMockedDimensionKey(conditionTagId, 2, "111")}; -// -// LogEvent event2(tagId, bucketStartTimeNs + 10); -// event2.write("222"); // uid -// event2.init(); -// ConditionKey key2; -// key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = -// {getMockedDimensionKey(conditionTagId, 2, "222")}; -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); -// -// EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); -// -// CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, -// bucketStartTimeNs, bucketStartTimeNs); -// -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); -// countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); -// countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); -// EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// countProducer.mPastBuckets.end()); -// const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets.size()); -// const auto& bucketInfo = buckets[0]; -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); -// EXPECT_EQ(1LL, bucketInfo.mCount); -//} -// -//TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { -// sp<AlarmMonitor> alarmMonitor; -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; -// -// int tagId = 1; -// int conditionTagId = 2; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// Alert alert; -// alert.set_num_buckets(3); -// alert.set_trigger_if_sum_gt(2); -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.write("111"); // uid -// event1.init(); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, -// bucketStartTimeNs, bucketStartTimeNs); -// -// sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); -// EXPECT_TRUE(anomalyTracker != nullptr); -// -// // Bucket is flushed yet. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// // App upgrade forces bucket flush. -// // Check that there's a past bucket and the bucket end is not adjusted. -// countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ((long long)bucketStartTimeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); -// EXPECT_EQ((long long)eventUpgradeTimeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); -// EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); -// // Anomaly tracker only contains full buckets. -// EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs(); -// // Next event occurs in same bucket as partial bucket created. -// LogEvent event2(tagId, bucketStartTimeNs + 59 * NS_PER_SEC + 10); -// event2.write("222"); // uid -// event2.init(); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); -// EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// // Third event in following bucket. -// LogEvent event3(tagId, bucketStartTimeNs + 62 * NS_PER_SEC + 10); -// event3.write("333"); // uid -// event3.init(); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); -// EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -//} -// -//TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; -// -// int tagId = 1; -// int conditionTagId = 2; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.write("111"); // uid -// event1.init(); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, -// bucketStartTimeNs, bucketStartTimeNs); -// -// // Bucket is flushed yet. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// -// // App upgrade forces bucket flush. -// // Check that there's a past bucket and the bucket end is not adjusted. -// countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ((int64_t)bucketStartTimeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); -// EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); -// -// // Next event occurs in same bucket as partial bucket created. -// LogEvent event2(tagId, bucketStartTimeNs + 70 * NS_PER_SEC + 10); -// event2.write("222"); // uid -// event2.init(); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); -// EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// -// // Third event in following bucket. -// LogEvent event3(tagId, bucketStartTimeNs + 121 * NS_PER_SEC + 10); -// event3.write("333"); // uid -// event3.init(); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); -// EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ((int64_t)eventUpgradeTimeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs); -//} -// -//TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { -// sp<AlarmMonitor> alarmMonitor; -// Alert alert; -// alert.set_id(11); -// alert.set_metric_id(1); -// alert.set_trigger_if_sum_gt(2); -// alert.set_num_buckets(2); -// const int32_t refPeriodSec = 1; -// alert.set_refractory_period_secs(refPeriodSec); -// -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; -// int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// bucketStartTimeNs, bucketStartTimeNs); -// -// sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); -// -// int tagId = 1; -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.init(); -// LogEvent event2(tagId, bucketStartTimeNs + 2); -// event2.init(); -// LogEvent event3(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1); -// event3.init(); -// LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1); -// event4.init(); -// LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2); -// event5.init(); -// LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3); -// event6.init(); -// LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC); -// event7.init(); -// -// // Two events in bucket #0. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); -// -// EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); -// EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); -// -// // One event in bucket #2. No alarm as bucket #0 is trashed out. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); -// EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); -// EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); -// -// // Two events in bucket #3. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6); -// EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); -// EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second); -// // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6 -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), -// std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); -// -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7); -// EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); -// EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), -// std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); -//} +namespace { + +void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); +} + +void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeString(statsEvent, uid.c_str()); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); +} + +} // namespace + +TEST(CountMetricProducerTest, TestFirstBucket) { + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5, + 600 * NS_PER_SEC + NS_PER_SEC / 2); + EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(10, countProducer.mCurrentBucketNum); + EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs()); +} + +TEST(CountMetricProducerTest, TestNonDimensionalEvents) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; + int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; + int tagId = 1; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + bucketStartTimeNs, bucketStartTimeNs); + + // 2 events in bucket 1. + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); + + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + + // Flushes at event #2. + countProducer.flushIfNeededLocked(bucketStartTimeNs + 2); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + // Flushes. + countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + countProducer.mPastBuckets.end()); + const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(2LL, buckets[0].mCount); + + // 1 matched event happens in bucket 2. + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 2, tagId); + + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + + countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + countProducer.mPastBuckets.end()); + EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1]; + EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs); + EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs); + EXPECT_EQ(1LL, bucketInfo2.mCount); + + // nothing happens in bucket 3. we should not record anything for bucket 3. + countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + countProducer.mPastBuckets.end()); + const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(2UL, buckets3.size()); +} + +TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_condition(StringToId("SCREEN_ON")); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, + bucketStartTimeNs); + + countProducer.onConditionChanged(true, bucketStartTimeNs); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1); + countProducer.onMatchedLogEvent(1 /*matcher index*/, event1); + + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2); + + // Upon this match event, the matched event1 is flushed. + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 10, /*atomId=*/1); + countProducer.onMatchedLogEvent(1 /*matcher index*/, event2); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + countProducer.mPastBuckets.end()); + + const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + const auto& bucketInfo = buckets[0]; + EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); + EXPECT_EQ(1LL, bucketInfo.mCount); +} + +TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + int tagId = 1; + int conditionTagId = 2; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); + MetricConditionLink* link = metric.add_links(); + link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); + buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what()); + buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition()); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); + + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 10, tagId, /*uid=*/"222"); + + ConditionKey key1; + key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = { + getMockedDimensionKey(conditionTagId, 2, "111")}; + + ConditionKey key2; + key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = { + getMockedDimensionKey(conditionTagId, 2, "222")}; + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); + + EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); + + CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, + bucketStartTimeNs, bucketStartTimeNs); + + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + countProducer.mPastBuckets.end()); + const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + const auto& bucketInfo = buckets[0]; + EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); + EXPECT_EQ(1LL, bucketInfo.mCount); +} + +TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { + sp<AlarmMonitor> alarmMonitor; + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + + int tagId = 1; + int conditionTagId = 2; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + Alert alert; + alert.set_num_buckets(3); + alert.set_trigger_if_sum_gt(2); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, + bucketStartTimeNs, bucketStartTimeNs); + + sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); + EXPECT_TRUE(anomalyTracker != nullptr); + + // Bucket is flushed yet. + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // App upgrade forces bucket flush. + // Check that there's a past bucket and the bucket end is not adjusted. + countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((long long)bucketStartTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ((long long)eventUpgradeTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + // Anomaly tracker only contains full buckets. + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs(); + // Next event occurs in same bucket as partial bucket created. + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Third event in following bucket. + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); +} + +TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + + int tagId = 1; + int conditionTagId = 2; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, + bucketStartTimeNs, bucketStartTimeNs); + + // Bucket is flushed yet. + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + // App upgrade forces bucket flush. + // Check that there's a past bucket and the bucket end is not adjusted. + countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((int64_t)bucketStartTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + + // Next event occurs in same bucket as partial bucket created. + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 70 * NS_PER_SEC + 10, tagId, /*uid=*/"222"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + + // Third event in following bucket. + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((int64_t)eventUpgradeTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs); +} + +TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { + sp<AlarmMonitor> alarmMonitor; + Alert alert; + alert.set_id(11); + alert.set_metric_id(1); + alert.set_trigger_if_sum_gt(2); + alert.set_num_buckets(2); + const int32_t refPeriodSec = 1; + alert.set_refractory_period_secs(refPeriodSec); + + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; + int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + bucketStartTimeNs, bucketStartTimeNs); + + sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + 2 * bucketSizeNs + 1, tagId); + LogEvent event4(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event4, bucketStartTimeNs + 3 * bucketSizeNs + 1, tagId); + LogEvent event5(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event5, bucketStartTimeNs + 3 * bucketSizeNs + 2, tagId); + LogEvent event6(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event6, bucketStartTimeNs + 3 * bucketSizeNs + 3, tagId); + LogEvent event7(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event7, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, tagId); + + // Two events in bucket #0. + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + + EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); + EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); + + // One event in bucket #2. No alarm as bucket #0 is trashed out. + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); + EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); + + // Two events in bucket #3. + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6); + EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); + EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second); + // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6 + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), + std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); + + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7); + EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); + EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), + std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); +} TEST(CountMetricProducerTest, TestOneWeekTimeUnit) { CountMetric metric; diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 66613749aaea..6143dc0dc5d1 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -13,17 +13,20 @@ // limitations under the License. #include "src/metrics/DurationMetricProducer.h" -#include "src/stats_log_util.h" -#include "metrics_test_helper.h" -#include "src/condition/ConditionWizard.h" #include <gmock/gmock.h> #include <gtest/gtest.h> #include <stdio.h> + #include <set> #include <unordered_map> #include <vector> +#include "metrics_test_helper.h" +#include "src/condition/ConditionWizard.h" +#include "src/stats_log_util.h" +#include "stats_event.h" + using namespace android::os::statsd; using namespace testing; using android::sp; @@ -39,6 +42,22 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); +namespace { + +void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); +} + +} // namespace + TEST(DurationMetricTrackerTest, TestFirstBucket) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); DurationMetric metric; @@ -56,383 +75,386 @@ TEST(DurationMetricTrackerTest, TestFirstBucket) { EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs()); } -// TODO(b/149590301): Update these to use new socket schema. -//TEST(DurationMetricTrackerTest, TestNoCondition) { -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// int tagId = 1; -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.init(); -// LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2); -// event2.init(); -// -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// durationProducer.onMatchedLogEvent(1 /* start index*/, event1); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); -// EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); -// EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// durationProducer.mPastBuckets.end()); -// const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(2UL, buckets.size()); -// EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs); -// EXPECT_EQ(2LL, buckets[1].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// int tagId = 1; -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.init(); -// LogEvent event2(tagId, bucketStartTimeNs + 2); -// event2.init(); -// LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); -// event3.init(); -// LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); -// event4.init(); -// -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// durationProducer.mCondition = ConditionState::kFalse; -// -// EXPECT_FALSE(durationProducer.mCondition); -// EXPECT_FALSE(durationProducer.isConditionSliced()); -// -// durationProducer.onMatchedLogEvent(1 /* start index*/, event1); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// -// durationProducer.onMatchedLogEvent(1 /* start index*/, event3); -// durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); -// EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); -// EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// durationProducer.mPastBuckets.end()); -// const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets2.size()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); -// EXPECT_EQ(1LL, buckets2[0].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// int tagId = 1; -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.init(); -// LogEvent event2(tagId, bucketStartTimeNs + 2); -// event2.init(); -// LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); -// event3.init(); -// LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); -// event4.init(); -// -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); -// EXPECT_FALSE(durationProducer.isConditionSliced()); -// -// durationProducer.onMatchedLogEvent(1 /* start index*/, event1); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// -// durationProducer.onMatchedLogEvent(1 /* start index*/, event3); -// durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); -// EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); -// const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets2.size()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); -// EXPECT_EQ(1LL, buckets2[0].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { -// /** -// * The duration starts from the first bucket, through the two partial buckets (10-70sec), -// * another bucket, and ends at the beginning of the next full bucket. -// * Expected buckets: -// * - [10,25]: 14 secs -// * - [25,70]: All 45 secs -// * - [70,130]: All 60 secs -// * - [130, 210]: Only 5 secs (event ended at 135sec) -// */ -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; -// int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; -// int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; -// -// int tagId = 1; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// LogEvent start_event(tagId, startTimeNs); -// start_event.init(); -// durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// std::vector<DurationBucket> buckets = -// durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration); -// EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. -// LogEvent end_event(tagId, endTimeNs); -// end_event.init(); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); -// buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(3UL, buckets.size()); -// EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); -// EXPECT_EQ(bucketSizeNs, buckets[2].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { -// /** -// * Expected buckets (start at 11s, upgrade at 75s, end at 135s): -// * - [10,70]: 59 secs -// * - [70,75]: 5 sec -// * - [75,130]: 55 secs -// */ -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; -// int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; -// int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; -// -// int tagId = 1; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// LogEvent start_event(tagId, startTimeNs); -// start_event.init(); -// durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// std::vector<DurationBucket> buckets = -// durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); -// EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs); -// EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); -// EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. -// LogEvent end_event(tagId, endTimeNs); -// end_event.init(); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); -// buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(3UL, buckets.size()); -// EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { -// sp<AlarmMonitor> alarmMonitor; -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; -// int64_t startTimeNs = bucketStartTimeNs + 1; -// int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; -// -// int tagId = 1; -// -// // Setup metric with alert. -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// Alert alert; -// alert.set_num_buckets(3); -// alert.set_trigger_if_sum_gt(2); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); -// EXPECT_TRUE(anomalyTracker != nullptr); -// -// LogEvent start_event(tagId, startTimeNs); -// start_event.init(); -// durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); -// durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. -// LogEvent end_event(tagId, endTimeNs); -// end_event.init(); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); -// -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, -// anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -//} -// -//TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; -// int64_t startTimeNs = bucketStartTimeNs + 1; -// int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; -// -// int tagId = 1; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); -// LogEvent event1(tagId, startTimeNs); -// event1.write("111"); // uid -// event1.init(); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// LogEvent start_event(tagId, startTimeNs); -// start_event.init(); -// durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. -// LogEvent end_event(tagId, endTimeNs); -// end_event.init(); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); -// std::vector<DurationBucket> buckets = -// durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets.size()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; -// int64_t startTimeNs = bucketStartTimeNs + 1; -// int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; -// -// int tagId = 1; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); -// LogEvent event1(tagId, startTimeNs); -// event1.write("111"); // uid -// event1.init(); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// LogEvent start_event(tagId, startTimeNs); -// start_event.init(); -// durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// // Stop occurs in the same partial bucket as created for the app upgrade. -// LogEvent end_event(tagId, endTimeNs); -// end_event.init(); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); -// std::vector<DurationBucket> buckets = -// durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets.size()); -// EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); -//} +TEST(DurationMetricTrackerTest, TestNoCondition) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + bucketSizeNs + 2, tagId); + + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); + EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + durationProducer.mPastBuckets.end()); + const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(2UL, buckets.size()); + EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs); + EXPECT_EQ(2LL, buckets[1].mDuration); +} + +TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId); + LogEvent event4(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId); + + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, 0 /* condition index */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + durationProducer.mCondition = ConditionState::kFalse; + + EXPECT_FALSE(durationProducer.mCondition); + EXPECT_FALSE(durationProducer.isConditionSliced()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event3); + durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); + EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + durationProducer.mPastBuckets.end()); + const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets2.size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); + EXPECT_EQ(1LL, buckets2[0].mDuration); +} + +TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId); + LogEvent event4(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId); + + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, 0 /* condition index */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); + EXPECT_FALSE(durationProducer.isConditionSliced()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event3); + durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); + const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets2.size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); + EXPECT_EQ(1LL, buckets2[0].mDuration); +} + +TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { + /** + * The duration starts from the first bucket, through the two partial buckets (10-70sec), + * another bucket, and ends at the beginning of the next full bucket. + * Expected buckets: + * - [10,25]: 14 secs + * - [25,70]: All 45 secs + * - [70,130]: All 60 secs + * - [130, 210]: Only 5 secs (event ended at 135sec) + */ + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; + int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + std::vector<DurationBucket> buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(3UL, buckets.size()); + EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); + EXPECT_EQ(bucketSizeNs, buckets[2].mDuration); +} + +TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { + /** + * Expected buckets (start at 11s, upgrade at 75s, end at 135s): + * - [10,70]: 59 secs + * - [70,75]: 5 sec + * - [75,130]: 55 secs + */ + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; + int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + std::vector<DurationBucket> buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); + EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(3UL, buckets.size()); + EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration); +} + +TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { + sp<AlarmMonitor> alarmMonitor; + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + int64_t startTimeNs = bucketStartTimeNs + 1; + int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); + + // Setup metric with alert. + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + Alert alert; + alert.set_num_buckets(3); + alert.set_trigger_if_sum_gt(2); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); + EXPECT_TRUE(anomalyTracker != nullptr); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, + anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); +} + +TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + int64_t startTimeNs = bucketStartTimeNs + 1; + int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); + std::vector<DurationBucket> buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); +} + +TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + int64_t startTimeNs = bucketStartTimeNs + 1; + int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // Stop occurs in the same partial bucket as created for the app upgrade. + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + std::vector<DurationBucket> buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index d416f1395727..e2eee032a43b 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -410,40 +410,75 @@ FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) return dimensions; } -// TODO(b/149590301): Update these helpers to use new socket schema. -//std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( -// const android::view::DisplayStateEnum state, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs); -// EXPECT_TRUE(event->write(state)); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>( -// android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs); -// EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON)); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>( -// android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs); -// EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF)); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent( -// int level, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs); -// EXPECT_TRUE(event->write(level)); -// event->init(); -// return event; -// -//} -// +std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( + uint64_t timestampNs, const android::view::DisplayStateEnum state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::SCREEN_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::BATTERY_SAVER_MODE_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::BATTERY_SAVER_MODE_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::SCREEN_BRIGHTNESS_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, level); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + //std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent( // const std::vector<AttributionNodeInternal>& attributions, const string& jobName, // const ScheduledJobStateChanged::State state, uint64_t timestampNs) { @@ -470,121 +505,212 @@ FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) // attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs); //} // -//std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, -// const WakelockStateChanged::State state, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs); -// event->write(attributions); -// event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); -// event->write(wakelockName); -// event->write(state); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateAcquireWakelockEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, -// uint64_t timestampNs) { -// return CreateWakelockStateChangedEvent( -// attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, -// uint64_t timestampNs) { -// return CreateWakelockStateChangedEvent( -// attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent( -// const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>( -// android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs); -// event->write(uid); -// event->write("pkg_name"); -// event->write("class_name"); -// event->write(state); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) { -// return CreateActivityForegroundStateChangedEvent( -// uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) { -// return CreateActivityForegroundStateChangedEvent( -// uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateSyncStateChangedEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& name, -// const SyncStateChanged::State state, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs); -// event->write(attributions); -// event->write(name); -// event->write(state); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateSyncStartEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& name, -// uint64_t timestampNs) { -// return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateSyncEndEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& name, -// uint64_t timestampNs) { -// return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent( -// const int uid, const ProcessLifeCycleStateChanged::State state, uint64_t timestampNs) { -// auto logEvent = std::make_unique<LogEvent>( -// android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs); -// logEvent->write(uid); -// logEvent->write(""); -// logEvent->write(state); -// logEvent->init(); -// return logEvent; -//} -// -//std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) { -// return CreateProcessLifeCycleStateChangedEvent( -// uid, ProcessLifeCycleStateChanged::CRASHED, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(const int uid, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::APP_CRASH_OCCURRED, timestampNs); -// event->write(uid); -// event->write("eventType"); -// event->write("processName"); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent( -// int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs) { -// auto logEvent = std::make_unique<LogEvent>( -// android::util::ISOLATED_UID_CHANGED, timestampNs); -// logEvent->write(hostUid); -// logEvent->write(isolatedUid); -// logEvent->write(is_create); -// logEvent->init(); -// return logEvent; -//} -// -//std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( -// int uid, const android::app::ProcessStateEnum state, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::UID_PROCESS_STATE_CHANGED, timestampNs); -// event->write(uid); -// event->write(state); -// event->init(); -// return event; -//} +std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& wakelockName, + const WakelockStateChanged::State state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::WAKELOCK_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); + AStatsEvent_writeString(statsEvent, wakelockName.c_str()); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& wakelockName) { + return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags, + wakelockName, WakelockStateChanged::ACQUIRE); +} + +std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& wakelockName) { + return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags, + wakelockName, WakelockStateChanged::RELEASE); +} + +std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent( + uint64_t timestampNs, const int uid, const ActivityForegroundStateChanged::State state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, "pkg_name"); + AStatsEvent_writeString(statsEvent, "class_name"); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid) { + return CreateActivityForegroundStateChangedEvent(timestampNs, uid, + ActivityForegroundStateChanged::BACKGROUND); +} + +std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid) { + return CreateActivityForegroundStateChangedEvent(timestampNs, uid, + ActivityForegroundStateChanged::FOREGROUND); +} + +std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& name, + const SyncStateChanged::State state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::SYNC_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeString(statsEvent, name.c_str()); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& name) { + return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, + SyncStateChanged::ON); +} + +std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& name) { + return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, + SyncStateChanged::OFF); +} + +std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent( + uint64_t timestampNs, const int uid, const ProcessLifeCycleStateChanged::State state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, ""); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, const int uid) { + return CreateProcessLifeCycleStateChangedEvent(timestampNs, uid, + ProcessLifeCycleStateChanged::CRASHED); +} + +std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::APP_CRASH_OCCURRED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, "eventType"); + AStatsEvent_writeString(statsEvent, "processName"); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid, + int isolatedUid, bool is_create) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::ISOLATED_UID_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, hostUid); + AStatsEvent_writeInt32(statsEvent, isolatedUid); + AStatsEvent_writeInt32(statsEvent, is_create); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( + uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::UID_PROCESS_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs, const StatsdConfig& config, const ConfigKey& key, diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index c8326eef5698..437101578397 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -166,11 +166,10 @@ FieldMatcher CreateAttributionUidDimensions(const int atomId, // Create log event for screen state changed. std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( - const android::view::DisplayStateEnum state, uint64_t timestampNs); + uint64_t timestampNs, const android::view::DisplayStateEnum state); // Create log event for screen brightness state changed. -std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent( - int level, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level); // Create log event when scheduled job starts. std::unique_ptr<LogEvent> CreateStartScheduledJobEvent( @@ -188,45 +187,42 @@ std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs); std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs); // Create log event for app moving to background. -std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid); // Create log event for app moving to foreground. -std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid); // Create log event when the app sync starts. -std::unique_ptr<LogEvent> CreateSyncStartEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& name, - uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, const vector<int>& uids, + const vector<string>& tags, const string& name); // Create log event when the app sync ends. -std::unique_ptr<LogEvent> CreateSyncEndEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& name, - uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, const vector<int>& uids, + const vector<string>& tags, const string& name); // Create log event when the app sync ends. -std::unique_ptr<LogEvent> CreateAppCrashEvent( - const int uid, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, const int uid); // Create log event for an app crash. -std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(const int uid, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid); // Create log event for acquiring wakelock. -std::unique_ptr<LogEvent> CreateAcquireWakelockEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, - uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(uint64_t timestampNs, const vector<int>& uids, + const vector<string>& tags, + const string& wakelockName); // Create log event for releasing wakelock. -std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, - uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(uint64_t timestampNs, const vector<int>& uids, + const vector<string>& tags, + const string& wakelockName); // Create log event for releasing wakelock. -std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent( - int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid, + int isolatedUid, bool is_create); // Create log event for uid process state change. std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( - int uid, const android::app::ProcessStateEnum state, uint64_t timestampNs); + uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state); // Helper function to create an AttributionNodeInternal proto. AttributionNodeInternal CreateAttribution(const int& uid, const string& tag); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 3a3eea966a86..e7036bb7f374 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -48,6 +48,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.KeyEvent; +import android.view.SurfaceControl; import android.view.SurfaceView; import android.view.WindowManager; import android.view.WindowManagerImpl; @@ -1937,8 +1938,8 @@ public abstract class AccessibilityService extends Service { * to declare the capability to take screenshot by setting the * {@link android.R.styleable#AccessibilityService_canTakeScreenshot} * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. - * Besides, This API is only supported for default display now - * {@link Display#DEFAULT_DISPLAY}. + * This API only will support {@link Display#DEFAULT_DISPLAY} until {@link SurfaceControl} + * supports non-default displays. * </p> * * @param displayId The logic display id, must be {@link Display#DEFAULT_DISPLAY} for @@ -1948,11 +1949,17 @@ public abstract class AccessibilityService extends Service { * * @return {@code true} if the taking screenshot accepted, {@code false} if too little time * has elapsed since the last screenshot, invalid display or internal errors. + * @throws IllegalArgumentException if displayId is not {@link Display#DEFAULT_DISPLAY}. */ public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<ScreenshotResult> callback) { Preconditions.checkNotNull(executor, "executor cannot be null"); Preconditions.checkNotNull(callback, "callback cannot be null"); + + if (displayId != Display.DEFAULT_DISPLAY) { + throw new IllegalArgumentException("DisplayId isn't the default display"); + } + final IAccessibilityServiceConnection connection = AccessibilityInteractionClient.getInstance().getConnection( mConnectionId); diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index ca37e9b107a0..2c41e8d0925a 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -18,6 +18,7 @@ package android.animation; import android.annotation.CallSuper; import android.annotation.IntDef; +import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; @@ -269,6 +270,11 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio private float mDurationScale = -1f; /** + * Animation handler used to schedule updates for this animation. + */ + private AnimationHandler mAnimationHandler; + + /** * Public constants */ @@ -1684,6 +1690,15 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * @hide */ public AnimationHandler getAnimationHandler() { - return AnimationHandler.getInstance(); + return mAnimationHandler != null ? mAnimationHandler : AnimationHandler.getInstance(); + } + + /** + * Sets the animation handler used to schedule updates for this animator or {@code null} to use + * the default handler. + * @hide + */ + public void setAnimationHandler(@Nullable AnimationHandler animationHandler) { + mAnimationHandler = animationHandler; } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 8d6bc7222abd..6480a6abeb78 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5829,7 +5829,7 @@ public class Activity extends ContextThemeWrapper intent.prepareToLeaveProcess(this); result = ActivityTaskManager.getService() .startActivity(mMainThread.getApplicationThread(), getBasePackageName(), - getFeatureId(), intent, + getAttributionTag(), intent, intent.resolveTypeIfNeeded(getContentResolver()), mToken, mEmbeddedID, requestCode, ActivityManager.START_FLAG_ONLY_IF_NEEDED, null, options); } catch (RemoteException e) { @@ -6624,12 +6624,10 @@ public class Activity extends ContextThemeWrapper String packageName = getPackageName(); try { data.prepareToLeaveProcess(this); - IIntentSender target = - ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, getFeatureId(), - mParent == null ? mToken : mParent.mToken, - mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null, - getUserId()); + IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( + ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, getAttributionTag(), + mParent == null ? mToken : mParent.mToken, mEmbeddedID, requestCode, + new Intent[]{data}, null, flags, null, getUserId()); return target != null ? new PendingIntent(target) : null; } catch (RemoteException e) { // Empty diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index dd4788e1146a..1a92b758ab9f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4243,6 +4243,7 @@ public class ActivityManager { * @hide */ @SystemApi + @TestApi @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String mcc, @NonNull String mnc) { if (mcc == null || mnc == null) { diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md new file mode 100644 index 000000000000..bee701addca8 --- /dev/null +++ b/core/java/android/app/AppOps.md @@ -0,0 +1,212 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License + --> + +# App-ops + +App-ops are used for two purposes: Access control and tracking. + +App-ops cover a wide variety of functionality from helping with runtime permissions to battery +consumption tracking. + +App-ops are defined in `AppOpsManager` as `OP_...` and need to be continuously numbered. The +integer values of the app-ops are not exposed. For app-ops visible to 3rd party apps, +the name of the app-op might be exposed as `OPSTR_`. As the integers are not part of the API, they +might (and have) changed between platform versions and OEM implementations. +`AppOpsManager.opToPublicName` and `AppOpsManager.strOpToOp` allow for conversion between integer +and string identifier for the op. + +## App-ops as access restrictions + +App-ops can either be controlled for each [uid](../os/Users.md#int-uid) or for each package. Which +one is used depends on the API provider maintaining this app-op. + +For any security or privacy related app-ops the provider needs to control the app-op per uid +as all security and privacy is based on uid in Android. + +App-op used for non-security related tasks are usually controlled per package to provide finer +granularity. + +### Setting the app-op mode + +To control access the app-op can be set to: + +`MODE_DEFAULT` +: Default behavior, might differ from app-op to app-op + +`MODE_ALLOWED` +: Allow the access + +`MODE_FOREGROUND` +: Allow the access but only if the app is currently in the [foreground](#foreground) + +`MODE_IGNORED` +: Don't allow the access, i.e. don't perform the requested action or return dummy data + +`MODE_ERRORED` +: Throw a `SecurityException` on access. This can be suppressed by using a `...noThrow` method to +check the mode + +The initial state of an app-op is defined in `AppOpsManager.sOpDefaultMode`. Confusingly the +initial state is often not `MODE_DEFAULT` + +Per-package modes can be set using `AppOpsManager.setMode` and per-uid modes can be set using +`AppOpsManager.setUidMode`. + +**Warning**: Do not use `setMode` and `setUidMode` for the same app-op. Due to the way the +internal storage for the mode works this can lead to very confusing behavior. If this ever happened +by accident this needs to be cleaned up for any affected user as the app-op mode is retained over +reboot. + +App-ops can also be set via the shell using the `appops set` command. The target package/uid can be +defined via parameters to this command. + +The current state of the app-op can be read via the `appops get` command or via `dumpsys appops`. +If the app-op is not mentioned in the output the app-op is in it's initial state. + +For example `dumpsys appops`: +``` +[...] + Uid 2000: + [...] + COARSE_LOCATION: mode=foreground + START_FOREGROUND: mode=foreground + LEGACY_STORAGE: mode=ignore + [...] +``` + +### Guarding access based on app-ops + +API providers need to check the mode returned by `AppOpsManager.noteOp` if they are are allowing +access to operations gated by the app-op. `AppOpsManager.unsafeCheckOp` should be used to check the +mode if no access is granted. E.g. this can be for displaying app-op state in the UI or when +checking the state before later calling `noteOp` anyway. + +If an operation refers to a time span (e.g. a audio-recording session) the API provider should +use `AppOpsManager.startOp` and `AppOpsManager.finishOp` instead of `noteOp`. + +`noteOp` and `startOp` take a `packageName` and `featureId` parameter. These need to be read from +the calling apps context as `Context.getOpPackageName` and `Context.getFeatureId`, then send to +the data provider and then passed on the `noteOp`/`startOp` method. + +#### App-ops and permissions + +Access guarding is often done in combination with permissions using [runtime permissions +](../permission/Permissions.md#runtime-permissions-and-app-ops) or [app-op permissions +](../permission/Permissions.md#app-op-permissions). This is preferred over just using an app-op + as permissions a concept more familiar to app developers. + +### Foreground + +The `AppOpsService` tracks the apps' proc state (== foreground-ness) by following the +`ActivityManagerService`'s proc state. It reduces the possible proc states to only those needed +for app-ops. It also delays the changes by a _settle time_. This delay is needed as the proc state +can fluctuate when switching apps. By delaying the change the appops service is not affected by +those. + +The proc state is used for two use cases: Firstly, Tracking remembers the proc state for each +tracked event. Secondly, `noteOp`/`checkOp` calls for app-op that are set to `MODE_FOREGROUND` are +translated using the `AppOpsService.UidState.evalMode` method into `MODE_ALLOWED` when the app is +counted as foreground and `MODE_IGNORED` when the app is counted as background. `checkOpRaw` +calls are not affected. + +The current proc state for an app can be read from `dumpsys appops`. The tracking information can +be read from `dumpsys appops` + +``` +Uid u0a118: + state=fg + capability=6 +``` + +## App-ops for tracking + +App-ops track many important events, including all accesses to runtime permission protected +APIs. This is done by tracking when an app-op was noted or started. The tracked data can only be +read by system components. + +**Note:** Only `noteOp`/`startOp` calls are tracked; `unsafeCheckOp` is not tracked. Hence it is +important to eventually call `noteOp` or `startOp` when providing access to protected operations +or data. + +Some apps are forwarding access to other apps. E.g. an app might get the location from the +system's location provider and then send the location further to a 3rd app. In this case the +app passing on the data needs to call `AppOpsManager.noteProxyOp` to signal the access proxying. +This might also make sense inside of a single app if the access is forwarded between two features of +the app. In this case an app-op is noted for the forwarding app (proxy) and the app that received +the data (proxied). As any app can do it is important to track how much the system trusts this +proxy-access-tracking. For more details see `AppOpService.noteProxyOperation`. + +The tracking information can be read from `dumpsys appops` split by feature, proc state and +proxying information with the syntax + +``` +Package THE_PACKAGE_NAME: + AN_APP_OP (CURRENT_MODE): + FEATURE_ID (or null for default feature)=[ + ACCESS_OR_REJECT: [PROC_STATE-PROXYING_TAG] TIME proxy[INFO_ABOUT_PROXY IF_PROXY_ACCESS] +``` + +Example: + +``` +Package com.google.android.gms: + READ_CONTACTS (allow): + null=[ + Access: [fgsvc-s] 2020-02-14 14:24:10.559 (-3d23h15m43s642ms) + Access: [fgsvc-tp] 2020-02-14 14:23:58.189 (-3d23h15m56s12ms) + ] + apkappcontext=[ + Access: [fg-tp] 2020-02-17 14:24:54.721 (-23h14m59s480ms) + ] + com.google.android.gms.icing=[ + Access: [fgsvc-tpd] 2020-02-14 14:26:27.018 (-3d23h13m27s183ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null] + Access: [fg-tpd] 2020-02-18 02:26:08.711 (-11h13m45s490ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null] + Access: [bg-tpd] 2020-02-14 14:34:55.310 (-3d23h4m58s891ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null] + ] + MANAGE_EXTERNAL_STORAGE (default): + null=[ + Reject: [fg-s]2020-02-18 08:00:04.444 (-5h39m49s757ms) + Reject: [bg-s]2020-02-18 08:00:04.427 (-5h39m49s774ms) + ] +``` + +### Tracking an app's own private data accesses + +An app can register an `AppOpsManager.OnOpNotedCallback` to get informed about what accesses the +system is tracking for it. As each runtime permission has an associated app-op this API is +particularly useful for an app that want to find unexpected private data accesses. + +## Listening to app-op events + +System apps (with the appropriate permissions) can listen to most app-op events, such as + +`noteOp` +: `startWatchingNoted` + +`startOp`/`finishOp` +: `startWatchingActive` + +mode changes +: `startWatchingMode` + +[foreground](#foreground)-ness changes +: `startWatchingMode` using the `WATCH_FOREGROUND_CHANGES` flag + +Watching such events is only ever as good as the tracked events. E.g. if the audio provider does +not call `startOp` for a audio-session, the app's activeness for the record-audio app-op is not +changed. Further there were cases where app-ops were noted even though no data was accessed or +operation was performed. Hence before relying on the data from app-ops, double check if the data +is actually reliable. diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index eeb5d4122cc7..f6a79cd767da 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -27,6 +27,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.app.usage.UsageStatsManager; +import android.compat.Compatibility; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; @@ -159,8 +160,8 @@ import java.util.function.Supplier; * <p>Some apps are forwarding access to other apps. E.g. an app might get the location from the * system's location provider and then send the location further to a 3rd app. In this case the * app passing on the data needs to call {@link #noteProxyOp} to signal the access proxying. This - * might also make sense inside of a single app if the access is forwarded between two features of - * the app. + * might also make sense inside of a single app if the access is forwarded between two parts of + * the tagged with different attribution tags. * * <p>An app can register an {@link OnOpNotedCallback} to get informed about what accesses the * system is tracking for it. As each runtime permission has an associated app-op this API is @@ -385,6 +386,13 @@ public class AppOpsManager { */ public static final int WATCH_FOREGROUND_CHANGES = 1 << 0; + /** + * Flag for {@link #startWatchingMode} that causes the callback to happen on the switch-op + * instead the op the callback was registered. (This simulates pre-R behavior). + * + * @hide + */ + public static final int CALL_BACK_ON_SWITCHED_OP = 1 << 1; /** * Flag to determine whether we should log noteOp/startOp calls to make sure they @@ -1988,108 +1996,108 @@ public class AppOpsManager { }; /** - * This specifies whether each option should allow the system - * (and system ui) to bypass the user restriction when active. - */ - private static boolean[] sOpAllowSystemRestrictionBypass = new boolean[] { - true, //COARSE_LOCATION - true, //FINE_LOCATION - false, //GPS - false, //VIBRATE - false, //READ_CONTACTS - false, //WRITE_CONTACTS - false, //READ_CALL_LOG - false, //WRITE_CALL_LOG - false, //READ_CALENDAR - false, //WRITE_CALENDAR - true, //WIFI_SCAN - false, //POST_NOTIFICATION - false, //NEIGHBORING_CELLS - false, //CALL_PHONE - false, //READ_SMS - false, //WRITE_SMS - false, //RECEIVE_SMS - false, //RECEIVE_EMERGECY_SMS - false, //RECEIVE_MMS - false, //RECEIVE_WAP_PUSH - false, //SEND_SMS - false, //READ_ICC_SMS - false, //WRITE_ICC_SMS - false, //WRITE_SETTINGS - true, //SYSTEM_ALERT_WINDOW - false, //ACCESS_NOTIFICATIONS - false, //CAMERA - false, //RECORD_AUDIO - false, //PLAY_AUDIO - false, //READ_CLIPBOARD - false, //WRITE_CLIPBOARD - false, //TAKE_MEDIA_BUTTONS - false, //TAKE_AUDIO_FOCUS - false, //AUDIO_MASTER_VOLUME - false, //AUDIO_VOICE_VOLUME - false, //AUDIO_RING_VOLUME - false, //AUDIO_MEDIA_VOLUME - false, //AUDIO_ALARM_VOLUME - false, //AUDIO_NOTIFICATION_VOLUME - false, //AUDIO_BLUETOOTH_VOLUME - false, //WAKE_LOCK - false, //MONITOR_LOCATION - false, //MONITOR_HIGH_POWER_LOCATION - false, //GET_USAGE_STATS - false, //MUTE_MICROPHONE - true, //TOAST_WINDOW - false, //PROJECT_MEDIA - false, //ACTIVATE_VPN - false, //WALLPAPER - false, //ASSIST_STRUCTURE - false, //ASSIST_SCREENSHOT - false, //READ_PHONE_STATE - false, //ADD_VOICEMAIL - false, // USE_SIP - false, // PROCESS_OUTGOING_CALLS - false, // USE_FINGERPRINT - false, // BODY_SENSORS - false, // READ_CELL_BROADCASTS - false, // MOCK_LOCATION - false, // READ_EXTERNAL_STORAGE - false, // WRITE_EXTERNAL_STORAGE - false, // TURN_ON_SCREEN - false, // GET_ACCOUNTS - false, // RUN_IN_BACKGROUND - false, // AUDIO_ACCESSIBILITY_VOLUME - false, // READ_PHONE_NUMBERS - false, // REQUEST_INSTALL_PACKAGES - false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE - false, // INSTANT_APP_START_FOREGROUND - false, // ANSWER_PHONE_CALLS - false, // OP_RUN_ANY_IN_BACKGROUND - false, // OP_CHANGE_WIFI_STATE - false, // OP_REQUEST_DELETE_PACKAGES - false, // OP_BIND_ACCESSIBILITY_SERVICE - false, // ACCEPT_HANDOVER - false, // MANAGE_IPSEC_HANDOVERS - false, // START_FOREGROUND - true, // BLUETOOTH_SCAN - false, // USE_BIOMETRIC - false, // ACTIVITY_RECOGNITION - false, // SMS_FINANCIAL_TRANSACTIONS - false, // READ_MEDIA_AUDIO - false, // WRITE_MEDIA_AUDIO - false, // READ_MEDIA_VIDEO - false, // WRITE_MEDIA_VIDEO - false, // READ_MEDIA_IMAGES - false, // WRITE_MEDIA_IMAGES - false, // LEGACY_STORAGE - false, // ACCESS_ACCESSIBILITY - false, // READ_DEVICE_IDENTIFIERS - false, // ACCESS_MEDIA_LOCATION - false, // QUERY_ALL_PACKAGES - false, // MANAGE_EXTERNAL_STORAGE - false, // INTERACT_ACROSS_PROFILES - false, // ACTIVATE_PLATFORM_VPN - false, // LOADER_USAGE_STATS - false, // ACCESS_CALL_AUDIO - false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED + * In which cases should an app be allowed to bypass the {@link #setUserRestriction user + * restriction} for a certain app-op. + */ + private static RestrictionBypass[] sOpAllowSystemRestrictionBypass = new RestrictionBypass[] { + new RestrictionBypass(true, false), //COARSE_LOCATION + new RestrictionBypass(true, false), //FINE_LOCATION + null, //GPS + null, //VIBRATE + null, //READ_CONTACTS + null, //WRITE_CONTACTS + null, //READ_CALL_LOG + null, //WRITE_CALL_LOG + null, //READ_CALENDAR + null, //WRITE_CALENDAR + new RestrictionBypass(true, false), //WIFI_SCAN + null, //POST_NOTIFICATION + null, //NEIGHBORING_CELLS + null, //CALL_PHONE + null, //READ_SMS + null, //WRITE_SMS + null, //RECEIVE_SMS + null, //RECEIVE_EMERGECY_SMS + null, //RECEIVE_MMS + null, //RECEIVE_WAP_PUSH + null, //SEND_SMS + null, //READ_ICC_SMS + null, //WRITE_ICC_SMS + null, //WRITE_SETTINGS + new RestrictionBypass(true, false), //SYSTEM_ALERT_WINDOW + null, //ACCESS_NOTIFICATIONS + null, //CAMERA + new RestrictionBypass(false, true), //RECORD_AUDIO + null, //PLAY_AUDIO + null, //READ_CLIPBOARD + null, //WRITE_CLIPBOARD + null, //TAKE_MEDIA_BUTTONS + null, //TAKE_AUDIO_FOCUS + null, //AUDIO_MASTER_VOLUME + null, //AUDIO_VOICE_VOLUME + null, //AUDIO_RING_VOLUME + null, //AUDIO_MEDIA_VOLUME + null, //AUDIO_ALARM_VOLUME + null, //AUDIO_NOTIFICATION_VOLUME + null, //AUDIO_BLUETOOTH_VOLUME + null, //WAKE_LOCK + null, //MONITOR_LOCATION + null, //MONITOR_HIGH_POWER_LOCATION + null, //GET_USAGE_STATS + null, //MUTE_MICROPHONE + new RestrictionBypass(true, false), //TOAST_WINDOW + null, //PROJECT_MEDIA + null, //ACTIVATE_VPN + null, //WALLPAPER + null, //ASSIST_STRUCTURE + null, //ASSIST_SCREENSHOT + null, //READ_PHONE_STATE + null, //ADD_VOICEMAIL + null, // USE_SIP + null, // PROCESS_OUTGOING_CALLS + null, // USE_FINGERPRINT + null, // BODY_SENSORS + null, // READ_CELL_BROADCASTS + null, // MOCK_LOCATION + null, // READ_EXTERNAL_STORAGE + null, // WRITE_EXTERNAL_STORAGE + null, // TURN_ON_SCREEN + null, // GET_ACCOUNTS + null, // RUN_IN_BACKGROUND + null, // AUDIO_ACCESSIBILITY_VOLUME + null, // READ_PHONE_NUMBERS + null, // REQUEST_INSTALL_PACKAGES + null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE + null, // INSTANT_APP_START_FOREGROUND + null, // ANSWER_PHONE_CALLS + null, // OP_RUN_ANY_IN_BACKGROUND + null, // OP_CHANGE_WIFI_STATE + null, // OP_REQUEST_DELETE_PACKAGES + null, // OP_BIND_ACCESSIBILITY_SERVICE + null, // ACCEPT_HANDOVER + null, // MANAGE_IPSEC_HANDOVERS + null, // START_FOREGROUND + new RestrictionBypass(true, false), // BLUETOOTH_SCAN + null, // USE_BIOMETRIC + null, // ACTIVITY_RECOGNITION + null, // SMS_FINANCIAL_TRANSACTIONS + null, // READ_MEDIA_AUDIO + null, // WRITE_MEDIA_AUDIO + null, // READ_MEDIA_VIDEO + null, // WRITE_MEDIA_VIDEO + null, // READ_MEDIA_IMAGES + null, // WRITE_MEDIA_IMAGES + null, // LEGACY_STORAGE + null, // ACCESS_ACCESSIBILITY + null, // READ_DEVICE_IDENTIFIERS + null, // ACCESS_MEDIA_LOCATION + null, // QUERY_ALL_PACKAGES + null, // MANAGE_EXTERNAL_STORAGE + null, // INTERACT_ACROSS_PROFILES + null, // ACTIVATE_PLATFORM_VPN + null, // LOADER_USAGE_STATS + null, // ACCESS_CALL_AUDIO + null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED }; /** @@ -2485,11 +2493,11 @@ public class AppOpsManager { } /** - * Retrieve whether the op allows the system (and system ui) to - * bypass the user restriction. + * Retrieve whether the op allows to bypass the user restriction. + * * @hide */ - public static boolean opAllowSystemBypassRestriction(int op) { + public static RestrictionBypass opAllowSystemBypassRestriction(int op) { return sOpAllowSystemRestrictionBypass[op]; } @@ -2536,6 +2544,29 @@ public class AppOpsManager { } /** + * When to not enforce {@link #setUserRestriction restrictions}. + * + * @hide + */ + public static class RestrictionBypass { + /** Does the app need to be privileged to bypass the restriction */ + public boolean isPrivileged; + + /** + * Does the app need to have the EXEMPT_FROM_AUDIO_RESTRICTIONS permission to bypass the + * restriction + */ + public boolean isRecordAudioRestrictionExcept; + + public RestrictionBypass(boolean isPrivileged, boolean isRecordAudioRestrictionExcept) { + this.isPrivileged = isPrivileged; + this.isRecordAudioRestrictionExcept = isRecordAudioRestrictionExcept; + } + + public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(true, true); + } + + /** * Class holding all of the operation information associated with an app. * @hide */ @@ -2627,23 +2658,23 @@ public class AppOpsManager { private @IntRange(from = 0) int mUid; /** Package of the proxy that noted the op */ private @Nullable String mPackageName; - /** ID of the feature of the proxy that noted the op */ - private @Nullable String mFeatureId; + /** Attribution tag of the proxy that noted the op */ + private @Nullable String mAttributionTag; /** * Reinit existing object with new state. * * @param uid UID of the proxy app that noted the op * @param packageName Package of the proxy that noted the op - * @param featureId ID of the feature of the proxy that noted the op + * @param attributionTag attribution tag of the proxy that noted the op * * @hide */ public void reinit(@IntRange(from = 0) int uid, @Nullable String packageName, - @Nullable String featureId) { + @Nullable String attributionTag) { mUid = Preconditions.checkArgumentNonnegative(uid); mPackageName = packageName; - mFeatureId = featureId; + mAttributionTag = attributionTag; } @@ -2668,21 +2699,21 @@ public class AppOpsManager { * UID of the proxy app that noted the op * @param packageName * Package of the proxy that noted the op - * @param featureId - * ID of the feature of the proxy that noted the op + * @param attributionTag + * Attribution tag of the proxy that noted the op * @hide */ @DataClass.Generated.Member public OpEventProxyInfo( @IntRange(from = 0) int uid, @Nullable String packageName, - @Nullable String featureId) { + @Nullable String attributionTag) { this.mUid = uid; com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mUid, "from", 0); this.mPackageName = packageName; - this.mFeatureId = featureId; + this.mAttributionTag = attributionTag; // onConstructed(); // You can define this method to get a callback } @@ -2696,7 +2727,7 @@ public class AppOpsManager { public OpEventProxyInfo(@NonNull OpEventProxyInfo orig) { mUid = orig.mUid; mPackageName = orig.mPackageName; - mFeatureId = orig.mFeatureId; + mAttributionTag = orig.mAttributionTag; } /** @@ -2716,11 +2747,11 @@ public class AppOpsManager { } /** - * ID of the feature of the proxy that noted the op + * Attribution tag of the proxy that noted the op */ @DataClass.Generated.Member - public @Nullable String getFeatureId() { - return mFeatureId; + public @Nullable String getAttributionTag() { + return mAttributionTag; } @Override @@ -2731,11 +2762,11 @@ public class AppOpsManager { byte flg = 0; if (mPackageName != null) flg |= 0x2; - if (mFeatureId != null) flg |= 0x4; + if (mAttributionTag != null) flg |= 0x4; dest.writeByte(flg); dest.writeInt(mUid); if (mPackageName != null) dest.writeString(mPackageName); - if (mFeatureId != null) dest.writeString(mFeatureId); + if (mAttributionTag != null) dest.writeString(mAttributionTag); } @Override @@ -2752,14 +2783,14 @@ public class AppOpsManager { byte flg = in.readByte(); int uid = in.readInt(); String packageName = (flg & 0x2) == 0 ? null : in.readString(); - String featureId = (flg & 0x4) == 0 ? null : in.readString(); + String attributionTag = (flg & 0x4) == 0 ? null : in.readString(); this.mUid = uid; com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mUid, "from", 0); this.mPackageName = packageName; - this.mFeatureId = featureId; + this.mAttributionTag = attributionTag; // onConstructed(); // You can define this method to get a callback } @@ -2783,7 +2814,7 @@ public class AppOpsManager { time = 1576814974615L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java", - inputSignatures = "private @android.annotation.IntRange(from=0L) int mUid\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.Nullable java.lang.String mFeatureId\npublic void reinit(int,java.lang.String,java.lang.String)\nclass OpEventProxyInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genHiddenCopyConstructor=true)") + inputSignatures = "private @android.annotation.IntRange(from=0L) int mUid\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\npublic void reinit(int,java.lang.String,java.lang.String)\nclass OpEventProxyInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genHiddenCopyConstructor=true)") @Deprecated private void __metadata() {} */ @@ -2982,7 +3013,7 @@ public class AppOpsManager { /** * Last {@link #noteOp} and {@link #startOp} events performed for a single op and a specific - * {@link Context#createFeatureContext(String) feature} for all uidModes and opFlags. + * {@link Context#createAttributionContext(String) attribution} for all uidModes and opFlags. * * @hide */ @@ -2991,7 +3022,7 @@ public class AppOpsManager { @Immutable // @DataClass(genHiddenConstructor = true) codegen verifier is broken @DataClass.Suppress({"getAccessEvents", "getRejectEvents", "getOp"}) - public static final class OpFeatureEntry implements Parcelable { + public static final class AttributedOpEntry implements Parcelable { /** The code of the op */ private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp; /** Whether the op is running */ @@ -3290,8 +3321,8 @@ public class AppOpsManager { } /** - * Gets the proxy info of the app that performed the last access on behalf of this feature - * and as a result blamed the op on this app. + * Gets the proxy info of the app that performed the last access on behalf of this + * attribution and as a result blamed the op on this attribution. * * @param flags The op flags * @@ -3308,7 +3339,7 @@ public class AppOpsManager { /** * Gets the proxy info of the app that performed the last foreground access on behalf of - * this feature and as a result blamed the op on this app. + * this attribution and as a result blamed the op on this attribution. * * @param flags The op flags * @@ -3326,7 +3357,7 @@ public class AppOpsManager { /** * Gets the proxy info of the app that performed the last background access on behalf of - * this feature and as a result blamed the op on this app. + * this attribution and as a result blamed the op on this attribution. * * @param flags The op flags * @@ -3343,8 +3374,8 @@ public class AppOpsManager { } /** - * Gets the proxy info of the app that performed the last access on behalf of this feature - * and as a result blamed the op on this app. + * Gets the proxy info of the app that performed the last access on behalf of this + * attribution and as a result blamed the op on this attribution. * * @param fromUidState The lowest UID state for which to query * @param toUidState The highest UID state for which to query (inclusive) @@ -3419,7 +3450,7 @@ public class AppOpsManager { /** - * Creates a new OpFeatureEntry. + * Creates a new OpAttributionEntry. * * @param op * The code of the op @@ -3432,7 +3463,7 @@ public class AppOpsManager { * @hide */ @DataClass.Generated.Member - public OpFeatureEntry( + public AttributedOpEntry( @IntRange(from = 0, to = _NUM_OP - 1) int op, boolean running, @Nullable LongSparseArray<NoteOpEvent> accessEvents, @@ -3502,7 +3533,7 @@ public class AppOpsManager { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ OpFeatureEntry(@NonNull Parcel in) { + /* package-private */ AttributedOpEntry(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -3525,16 +3556,16 @@ public class AppOpsManager { } @DataClass.Generated.Member - public static final @NonNull Parcelable.Creator<OpFeatureEntry> CREATOR - = new Parcelable.Creator<OpFeatureEntry>() { + public static final @NonNull Parcelable.Creator<AttributedOpEntry> CREATOR + = new Parcelable.Creator<AttributedOpEntry>() { @Override - public OpFeatureEntry[] newArray(int size) { - return new OpFeatureEntry[size]; + public AttributedOpEntry[] newArray(int size) { + return new AttributedOpEntry[size]; } @Override - public OpFeatureEntry createFromParcel(@NonNull Parcel in) { - return new OpFeatureEntry(in); + public AttributedOpEntry createFromParcel(@NonNull Parcel in) { + return new AttributedOpEntry(in); } }; @@ -3543,7 +3574,7 @@ public class AppOpsManager { time = 1574809856239L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java", - inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final boolean mRunning\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpFeatureEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mAccessEvents\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpFeatureEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mRejectEvents\npublic @android.annotation.NonNull android.util.ArraySet<java.lang.Long> collectKeys()\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic long getAccessTime(int,int)\npublic long getRejectTime(int,int)\npublic long getDuration(int,int)\npublic int getProxyUid(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyFeatureId(int,int)\nclass OpFeatureEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final boolean mRunning\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpAttributionEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mAccessEvents\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpAttributionEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mRejectEvents\npublic @android.annotation.NonNull android.util.ArraySet<java.lang.Long> collectKeys()\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic long getAccessTime(int,int)\npublic long getRejectTime(int,int)\npublic long getDuration(int,int)\npublic int getProxyUid(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyAttributionTag(int,int)\nclass OpAttributionEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)") @Deprecated private void __metadata() {} */ @@ -3569,8 +3600,8 @@ public class AppOpsManager { private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp; /** The mode of the op */ private final @Mode int mMode; - /** The features that have been used when checking the op */ - private final @NonNull Map<String, OpFeatureEntry> mFeatures; + /** The attributed entries by attribution tag */ + private final @NonNull Map<String, AttributedOpEntry> mAttributedOpEntries; /** * @hide @@ -3611,7 +3642,7 @@ public class AppOpsManager { * @see #getLastAccessForegroundTime(int) * @see #getLastAccessBackgroundTime(int) * @see #getLastAccessTime(int, int, int) - * @see OpFeatureEntry#getLastAccessTime(int) + * @see AttributedOpEntry#getLastAccessTime(int) */ public long getLastAccessTime(@OpFlags int flags) { return getLastAccessTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags); @@ -3628,7 +3659,7 @@ public class AppOpsManager { * @see #getLastAccessTime(int) * @see #getLastAccessBackgroundTime(int) * @see #getLastAccessTime(int, int, int) - * @see OpFeatureEntry#getLastAccessForegroundTime(int) + * @see AttributedOpEntry#getLastAccessForegroundTime(int) */ public long getLastAccessForegroundTime(@OpFlags int flags) { return getLastAccessTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp), @@ -3646,7 +3677,7 @@ public class AppOpsManager { * @see #getLastAccessTime(int) * @see #getLastAccessForegroundTime(int) * @see #getLastAccessTime(int, int, int) - * @see OpFeatureEntry#getLastAccessBackgroundTime(int) + * @see AttributedOpEntry#getLastAccessBackgroundTime(int) */ public long getLastAccessBackgroundTime(@OpFlags int flags) { return getLastAccessTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE, @@ -3663,13 +3694,14 @@ public class AppOpsManager { private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState, @UidState int toUidState, @OpFlags int flags) { NoteOpEvent lastAccessEvent = null; - for (OpFeatureEntry featureEntry : mFeatures.values()) { - NoteOpEvent lastFeatureAccessEvent = featureEntry.getLastAccessEvent(fromUidState, - toUidState, flags); - - if (lastAccessEvent == null || (lastFeatureAccessEvent != null - && lastFeatureAccessEvent.getNoteTime() > lastAccessEvent.getNoteTime())) { - lastAccessEvent = lastFeatureAccessEvent; + for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) { + NoteOpEvent lastAttributionAccessEvent = attributionEntry.getLastAccessEvent( + fromUidState, toUidState, flags); + + if (lastAccessEvent == null || (lastAttributionAccessEvent != null + && lastAttributionAccessEvent.getNoteTime() + > lastAccessEvent.getNoteTime())) { + lastAccessEvent = lastAttributionAccessEvent; } } @@ -3689,7 +3721,7 @@ public class AppOpsManager { * @see #getLastAccessTime(int) * @see #getLastAccessForegroundTime(int) * @see #getLastAccessBackgroundTime(int) - * @see OpFeatureEntry#getLastAccessTime(int, int, int) + * @see AttributedOpEntry#getLastAccessTime(int, int, int) */ public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState, @OpFlags int flags) { @@ -3725,7 +3757,7 @@ public class AppOpsManager { * @see #getLastRejectForegroundTime(int) * @see #getLastRejectBackgroundTime(int) * @see #getLastRejectTime(int, int, int) - * @see OpFeatureEntry#getLastRejectTime(int) + * @see AttributedOpEntry#getLastRejectTime(int) */ public long getLastRejectTime(@OpFlags int flags) { return getLastRejectTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags); @@ -3742,7 +3774,7 @@ public class AppOpsManager { * @see #getLastRejectTime(int) * @see #getLastRejectBackgroundTime(int) * @see #getLastRejectTime(int, int, int) - * @see OpFeatureEntry#getLastRejectForegroundTime(int) + * @see AttributedOpEntry#getLastRejectForegroundTime(int) */ public long getLastRejectForegroundTime(@OpFlags int flags) { return getLastRejectTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp), @@ -3760,7 +3792,7 @@ public class AppOpsManager { * @see #getLastRejectTime(int) * @see #getLastRejectForegroundTime(int) * @see #getLastRejectTime(int, int, int) - * @see OpFeatureEntry#getLastRejectBackgroundTime(int) + * @see AttributedOpEntry#getLastRejectBackgroundTime(int) */ public long getLastRejectBackgroundTime(@OpFlags int flags) { return getLastRejectTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE, @@ -3777,13 +3809,14 @@ public class AppOpsManager { private @Nullable NoteOpEvent getLastRejectEvent(@UidState int fromUidState, @UidState int toUidState, @OpFlags int flags) { NoteOpEvent lastAccessEvent = null; - for (OpFeatureEntry featureEntry : mFeatures.values()) { - NoteOpEvent lastFeatureAccessEvent = featureEntry.getLastRejectEvent(fromUidState, - toUidState, flags); - - if (lastAccessEvent == null || (lastFeatureAccessEvent != null - && lastFeatureAccessEvent.getNoteTime() > lastAccessEvent.getNoteTime())) { - lastAccessEvent = lastFeatureAccessEvent; + for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) { + NoteOpEvent lastAttributionAccessEvent = attributionEntry.getLastRejectEvent( + fromUidState, toUidState, flags); + + if (lastAccessEvent == null || (lastAttributionAccessEvent != null + && lastAttributionAccessEvent.getNoteTime() + > lastAccessEvent.getNoteTime())) { + lastAccessEvent = lastAttributionAccessEvent; } } @@ -3804,7 +3837,7 @@ public class AppOpsManager { * @see #getLastRejectForegroundTime(int) * @see #getLastRejectBackgroundTime(int) * @see #getLastRejectTime(int, int, int) - * @see OpFeatureEntry#getLastRejectTime(int, int, int) + * @see AttributedOpEntry#getLastRejectTime(int, int, int) */ public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState, @OpFlags int flags) { @@ -3820,8 +3853,8 @@ public class AppOpsManager { * @return Whether the operation is running. */ public boolean isRunning() { - for (OpFeatureEntry opFeatureEntry : mFeatures.values()) { - if (opFeatureEntry.isRunning()) { + for (AttributedOpEntry opAttributionEntry : mAttributedOpEntries.values()) { + if (opAttributionEntry.isRunning()) { return true; } } @@ -3847,7 +3880,7 @@ public class AppOpsManager { * @see #getLastForegroundDuration(int) * @see #getLastBackgroundDuration(int) * @see #getLastDuration(int, int, int) - * @see OpFeatureEntry#getLastDuration(int) + * @see AttributedOpEntry#getLastDuration(int) */ public long getLastDuration(@OpFlags int flags) { return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags); @@ -3863,7 +3896,7 @@ public class AppOpsManager { * @see #getLastDuration(int) * @see #getLastBackgroundDuration(int) * @see #getLastDuration(int, int, int) - * @see OpFeatureEntry#getLastForegroundDuration(int) + * @see AttributedOpEntry#getLastForegroundDuration(int) */ public long getLastForegroundDuration(@OpFlags int flags) { return getLastDuration(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp), @@ -3880,7 +3913,7 @@ public class AppOpsManager { * @see #getLastDuration(int) * @see #getLastForegroundDuration(int) * @see #getLastDuration(int, int, int) - * @see OpFeatureEntry#getLastBackgroundDuration(int) + * @see AttributedOpEntry#getLastBackgroundDuration(int) */ public long getLastBackgroundDuration(@OpFlags int flags) { return getLastDuration(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE, @@ -3899,7 +3932,7 @@ public class AppOpsManager { * @see #getLastDuration(int) * @see #getLastForegroundDuration(int) * @see #getLastBackgroundDuration(int) - * @see OpFeatureEntry#getLastDuration(int, int, int) + * @see AttributedOpEntry#getLastDuration(int, int, int) */ public long getLastDuration(@UidState int fromUidState, @UidState int toUidState, @OpFlags int flags) { @@ -3974,7 +4007,7 @@ public class AppOpsManager { * @see #getLastForegroundProxyInfo(int) * @see #getLastBackgroundProxyInfo(int) * @see #getLastProxyInfo(int, int, int) - * @see OpFeatureEntry#getLastProxyInfo(int) + * @see AttributedOpEntry#getLastProxyInfo(int) */ public @Nullable OpEventProxyInfo getLastProxyInfo(@OpFlags int flags) { return getLastProxyInfo(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags); @@ -3991,7 +4024,7 @@ public class AppOpsManager { * @see #getLastProxyInfo(int) * @see #getLastBackgroundProxyInfo(int) * @see #getLastProxyInfo(int, int, int) - * @see OpFeatureEntry#getLastForegroundProxyInfo(int) + * @see AttributedOpEntry#getLastForegroundProxyInfo(int) */ public @Nullable OpEventProxyInfo getLastForegroundProxyInfo(@OpFlags int flags) { return getLastProxyInfo(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp), @@ -4009,7 +4042,7 @@ public class AppOpsManager { * @see #getLastProxyInfo(int) * @see #getLastForegroundProxyInfo(int) * @see #getLastProxyInfo(int, int, int) - * @see OpFeatureEntry#getLastBackgroundProxyInfo(int) + * @see AttributedOpEntry#getLastBackgroundProxyInfo(int) */ public @Nullable OpEventProxyInfo getLastBackgroundProxyInfo(@OpFlags int flags) { return getLastProxyInfo(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE, @@ -4029,7 +4062,7 @@ public class AppOpsManager { * @see #getLastProxyInfo(int) * @see #getLastForegroundProxyInfo(int) * @see #getLastBackgroundProxyInfo(int) - * @see OpFeatureEntry#getLastProxyInfo(int, int, int) + * @see AttributedOpEntry#getLastProxyInfo(int, int, int) */ public @Nullable OpEventProxyInfo getLastProxyInfo(@UidState int fromUidState, @UidState int toUidState, @OpFlags int flags) { @@ -4063,15 +4096,15 @@ public class AppOpsManager { * The code of the op * @param mode * The mode of the op - * @param features - * The features that have been used when checking the op + * @param attributedOpEntries + * The attributions that have been used when noting the op * @hide */ @DataClass.Generated.Member public OpEntry( @IntRange(from = 0, to = _NUM_OP - 1) int op, @Mode int mode, - @NonNull Map<String,OpFeatureEntry> features) { + @NonNull Map<String, AttributedOpEntry> attributedOpEntries) { this.mOp = op; com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mOp, @@ -4080,9 +4113,9 @@ public class AppOpsManager { this.mMode = mode; com.android.internal.util.AnnotationValidations.validate( Mode.class, null, mMode); - this.mFeatures = features; + this.mAttributedOpEntries = attributedOpEntries; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mFeatures); + NonNull.class, null, mAttributedOpEntries); // onConstructed(); // You can define this method to get a callback } @@ -4096,14 +4129,14 @@ public class AppOpsManager { } /** - * The features that have been used when checking the op keyed by id of the feature. + * The attributed entries keyed by attribution tag. * - * @see Context#createFeatureContext(String) + * @see Context#createAttributionContext(String) * @see #noteOp(String, int, String, String, String) */ @DataClass.Generated.Member - public @NonNull Map<String,OpFeatureEntry> getFeatures() { - return mFeatures; + public @NonNull Map<String, AttributedOpEntry> getAttributedOpEntries() { + return mAttributedOpEntries; } @Override @@ -4114,7 +4147,7 @@ public class AppOpsManager { dest.writeInt(mOp); dest.writeInt(mMode); - dest.writeMap(mFeatures); + dest.writeMap(mAttributedOpEntries); } @Override @@ -4130,8 +4163,8 @@ public class AppOpsManager { int op = in.readInt(); int mode = in.readInt(); - Map<String,OpFeatureEntry> features = new java.util.LinkedHashMap<>(); - in.readMap(features, OpFeatureEntry.class.getClassLoader()); + Map<String, AttributedOpEntry> attributions = new java.util.LinkedHashMap<>(); + in.readMap(attributions, AttributedOpEntry.class.getClassLoader()); this.mOp = op; com.android.internal.util.AnnotationValidations.validate( @@ -4141,9 +4174,9 @@ public class AppOpsManager { this.mMode = mode; com.android.internal.util.AnnotationValidations.validate( Mode.class, null, mMode); - this.mFeatures = features; + this.mAttributedOpEntries = attributions; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mFeatures); + NonNull.class, null, mAttributedOpEntries); // onConstructed(); // You can define this method to get a callback } @@ -4167,7 +4200,7 @@ public class AppOpsManager { time = 1574809856259L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java", - inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final @android.app.Mode int mMode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.app.OpFeatureEntry> mFeatures\npublic @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getOpStr()}\") int getOp()\npublic @android.annotation.NonNull java.lang.String getOpStr()\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getAccessTime(int, int)}\") long getTime()\npublic @java.lang.Deprecated long getLastAccessTime(int)\npublic @java.lang.Deprecated long getLastAccessForegroundTime(int)\npublic @java.lang.Deprecated long getLastAccessBackgroundTime(int)\npublic @java.lang.Deprecated long getLastAccessTime(int,int,int)\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getLastRejectTime(int, int, int)}\") long getRejectTime()\npublic @java.lang.Deprecated long getLastRejectTime(int)\npublic @java.lang.Deprecated long getLastRejectForegroundTime(int)\npublic @java.lang.Deprecated long getLastRejectBackgroundTime(int)\npublic @java.lang.Deprecated long getLastRejectTime(int,int,int)\npublic long getAccessTime(int,int)\npublic long getRejectTime(int,int)\npublic boolean isRunning()\nprivate android.app.NoteOpEvent getLastAccessEvent(int,int,int)\npublic @java.lang.Deprecated long getDuration()\npublic @java.lang.Deprecated long getLastForegroundDuration(int)\npublic @java.lang.Deprecated long getLastBackgroundDuration(int)\npublic @java.lang.Deprecated long getLastDuration(int,int,int)\npublic @java.lang.Deprecated int getProxyUid()\npublic @java.lang.Deprecated @android.annotation.Nullable java.lang.String getProxyPackageName()\nprivate @android.app.UidState int getLastAccessUidStateForFlagsInStatesOfAllFeatures(int,int,int)\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\nprivate @android.app.UidState int getLastRejectUidStateForFlagsInStatesOfAllFeatures(int,int,int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic long getDuration(int,int)\npublic int getProxyUid(int,int)\nprivate int getProxyUid(int,int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\nprivate @android.annotation.Nullable java.lang.String getProxyPackageName(int,int,int)\nclass OpEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final @android.app.Mode int mMode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.app.OpAttributionEntry> mAttributions\npublic @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getOpStr()}\") int getOp()\npublic @android.annotation.NonNull java.lang.String getOpStr()\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getAccessTime(int, int)}\") long getTime()\npublic @java.lang.Deprecated long getLastAccessTime(int)\npublic @java.lang.Deprecated long getLastAccessForegroundTime(int)\npublic @java.lang.Deprecated long getLastAccessBackgroundTime(int)\npublic @java.lang.Deprecated long getLastAccessTime(int,int,int)\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getLastRejectTime(int, int, int)}\") long getRejectTime()\npublic @java.lang.Deprecated long getLastRejectTime(int)\npublic @java.lang.Deprecated long getLastRejectForegroundTime(int)\npublic @java.lang.Deprecated long getLastRejectBackgroundTime(int)\npublic @java.lang.Deprecated long getLastRejectTime(int,int,int)\npublic long getAccessTime(int,int)\npublic long getRejectTime(int,int)\npublic boolean isRunning()\nprivate android.app.NoteOpEvent getLastAccessEvent(int,int,int)\npublic @java.lang.Deprecated long getDuration()\npublic @java.lang.Deprecated long getLastForegroundDuration(int)\npublic @java.lang.Deprecated long getLastBackgroundDuration(int)\npublic @java.lang.Deprecated long getLastDuration(int,int,int)\npublic @java.lang.Deprecated int getProxyUid()\npublic @java.lang.Deprecated @android.annotation.Nullable java.lang.String getProxyPackageName()\nprivate @android.app.UidState int getLastAccessUidStateForFlagsInStatesOfAllAttributions(int,int,int)\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\nprivate @android.app.UidState int getLastRejectUidStateForFlagsInStatesOfAllAttributions(int,int,int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic long getDuration(int,int)\npublic int getProxyUid(int,int)\nprivate int getProxyUid(int,int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\nprivate @android.annotation.Nullable java.lang.String getProxyPackageName(int,int,int)\nclass OpEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)") @Deprecated private void __metadata() {} */ @@ -4183,7 +4216,7 @@ public class AppOpsManager { void visitHistoricalOps(@NonNull HistoricalOps ops); void visitHistoricalUidOps(@NonNull HistoricalUidOps ops); void visitHistoricalPackageOps(@NonNull HistoricalPackageOps ops); - void visitHistoricalFeatureOps(@NonNull HistoricalFeatureOps ops); + void visitHistoricalAttributionOps(@NonNull AttributedHistoricalOps ops); void visitHistoricalOp(@NonNull HistoricalOp ops); } @@ -4196,7 +4229,7 @@ public class AppOpsManager { @IntDef(flag = true, prefix = { "FILTER_BY_" }, value = { FILTER_BY_UID, FILTER_BY_PACKAGE_NAME, - FILTER_BY_FEATURE_ID, + FILTER_BY_ATTRIBUTION_TAG, FILTER_BY_OP_NAMES }) public @interface HistoricalOpsRequestFilter {} @@ -4216,11 +4249,11 @@ public class AppOpsManager { public static final int FILTER_BY_PACKAGE_NAME = 1<<1; /** - * Filter historical appop request by feature id. + * Filter historical appop request by attribution tag. * * @hide */ - public static final int FILTER_BY_FEATURE_ID = 1<<2; + public static final int FILTER_BY_ATTRIBUTION_TAG = 1<<2; /** * Filter historical appop request by op names. @@ -4241,7 +4274,7 @@ public class AppOpsManager { public static final class HistoricalOpsRequest { private final int mUid; private final @Nullable String mPackageName; - private final @Nullable String mFeatureId; + private final @Nullable String mAttributionTag; private final @Nullable List<String> mOpNames; private final @HistoricalOpsRequestFilter int mFilter; private final long mBeginTimeMillis; @@ -4249,12 +4282,12 @@ public class AppOpsManager { private final @OpFlags int mFlags; private HistoricalOpsRequest(int uid, @Nullable String packageName, - @Nullable String featureId, @Nullable List<String> opNames, + @Nullable String attributionTag, @Nullable List<String> opNames, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @OpFlags int flags) { mUid = uid; mPackageName = packageName; - mFeatureId = featureId; + mAttributionTag = attributionTag; mOpNames = opNames; mFilter = filter; mBeginTimeMillis = beginTimeMillis; @@ -4272,7 +4305,7 @@ public class AppOpsManager { public static final class Builder { private int mUid = Process.INVALID_UID; private @Nullable String mPackageName; - private @Nullable String mFeatureId; + private @Nullable String mAttributionTag; private @Nullable List<String> mOpNames; private @HistoricalOpsRequestFilter int mFilter; private final long mBeginTimeMillis; @@ -4336,14 +4369,14 @@ public class AppOpsManager { } /** - * Sets the feature id to query for. + * Sets the attribution tag to query for. * - * @param featureId The id of the feature. + * @param attributionTag attribution tag * @return This builder. */ - public @NonNull Builder setFeatureId(@Nullable String featureId) { - mFeatureId = featureId; - mFilter |= FILTER_BY_FEATURE_ID; + public @NonNull Builder setAttributionTag(@Nullable String attributionTag) { + mAttributionTag = attributionTag; + mFilter |= FILTER_BY_ATTRIBUTION_TAG; return this; } @@ -4394,7 +4427,7 @@ public class AppOpsManager { * @return a new {@link HistoricalOpsRequest}. */ public @NonNull HistoricalOpsRequest build() { - return new HistoricalOpsRequest(mUid, mPackageName, mFeatureId, mOpNames, + return new HistoricalOpsRequest(mUid, mPackageName, mAttributionTag, mOpNames, mFilter, mBeginTimeMillis, mEndTimeMillis, mFlags); } } @@ -4554,7 +4587,7 @@ public class AppOpsManager { * * @param uid Uid to filter for. * @param packageName Package to filter for. - * @param featureId Package to filter for. + * @param attributionTag attribution tag to filter for * @param opNames Ops to filter for. * @param filter Which parameters to filter on. * @param beginTimeMillis The begin time to filter for or {@link Long#MIN_VALUE} for all. @@ -4562,7 +4595,7 @@ public class AppOpsManager { * * @hide */ - public void filter(int uid, @Nullable String packageName, @Nullable String featureId, + public void filter(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis) { final long durationMillis = getDurationMillis(); @@ -4576,7 +4609,7 @@ public class AppOpsManager { if ((filter & FILTER_BY_UID) != 0 && uid != uidOp.getUid()) { mHistoricalUidOps.removeAt(i); } else { - uidOp.filter(packageName, featureId, opNames, filter, scaleFactor); + uidOp.filter(packageName, attributionTag, opNames, filter, scaleFactor); if (uidOp.getPackageCount() == 0) { mHistoricalUidOps.removeAt(i); } @@ -4607,28 +4640,28 @@ public class AppOpsManager { /** @hide */ @TestApi public void increaseAccessCount(int opCode, int uid, @NonNull String packageName, - @Nullable String featureId, @UidState int uidState, @OpFlags int flags, + @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalUidOps(uid).increaseAccessCount(opCode, - packageName, featureId, uidState, flags, increment); + packageName, attributionTag, uidState, flags, increment); } /** @hide */ @TestApi public void increaseRejectCount(int opCode, int uid, @NonNull String packageName, - @Nullable String featureId, @UidState int uidState, @OpFlags int flags, + @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalUidOps(uid).increaseRejectCount(opCode, - packageName, featureId, uidState, flags, increment); + packageName, attributionTag, uidState, flags, increment); } /** @hide */ @TestApi public void increaseAccessDuration(int opCode, int uid, @NonNull String packageName, - @Nullable String featureId, @UidState int uidState, @OpFlags int flags, + @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalUidOps(uid).increaseAccessDuration(opCode, - packageName, featureId, uidState, flags, increment); + packageName, attributionTag, uidState, flags, increment); } /** @hide */ @@ -4908,7 +4941,7 @@ public class AppOpsManager { } } - private void filter(@Nullable String packageName, @Nullable String featureId, + private void filter(@Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter, double fractionToRemove) { final int packageCount = getPackageCount(); @@ -4918,8 +4951,8 @@ public class AppOpsManager { packageOps.getPackageName())) { mHistoricalPackageOps.removeAt(i); } else { - packageOps.filter(featureId, opNames, filter, fractionToRemove); - if (packageOps.getFeatureCount() == 0) { + packageOps.filter(attributionTag, opNames, filter, fractionToRemove); + if (packageOps.getAttributedOpsCount() == 0) { mHistoricalPackageOps.removeAt(i); } } @@ -4938,24 +4971,24 @@ public class AppOpsManager { } private void increaseAccessCount(int opCode, @NonNull String packageName, - @Nullable String featureId, @UidState int uidState, @OpFlags int flags, + @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalPackageOps(packageName).increaseAccessCount( - opCode, featureId, uidState, flags, increment); + opCode, attributionTag, uidState, flags, increment); } private void increaseRejectCount(int opCode, @NonNull String packageName, - @Nullable String featureId, @UidState int uidState, @OpFlags int flags, + @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalPackageOps(packageName).increaseRejectCount( - opCode, featureId, uidState, flags, increment); + opCode, attributionTag, uidState, flags, increment); } private void increaseAccessDuration(int opCode, @NonNull String packageName, - @Nullable String featureId, @UidState int uidState, @OpFlags int flags, + @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalPackageOps(packageName).increaseAccessDuration( - opCode, featureId, uidState, flags, increment); + opCode, attributionTag, uidState, flags, increment); } /** @@ -5100,7 +5133,7 @@ public class AppOpsManager { @SystemApi public static final class HistoricalPackageOps implements Parcelable { private final @NonNull String mPackageName; - private @Nullable ArrayMap<String, HistoricalFeatureOps> mHistoricalFeatureOps; + private @Nullable ArrayMap<String, AttributedHistoricalOps> mAttributedHistoricalOps; /** @hide */ public HistoricalPackageOps(@NonNull String packageName) { @@ -5109,70 +5142,71 @@ public class AppOpsManager { private HistoricalPackageOps(@NonNull HistoricalPackageOps other) { mPackageName = other.mPackageName; - final int opCount = other.getFeatureCount(); + final int opCount = other.getAttributedOpsCount(); for (int i = 0; i < opCount; i++) { - final HistoricalFeatureOps origOps = other.getFeatureOpsAt(i); - final HistoricalFeatureOps cloneOps = new HistoricalFeatureOps(origOps); - if (mHistoricalFeatureOps == null) { - mHistoricalFeatureOps = new ArrayMap<>(opCount); + final AttributedHistoricalOps origOps = other.getAttributedOpsAt(i); + final AttributedHistoricalOps cloneOps = new AttributedHistoricalOps(origOps); + if (mAttributedHistoricalOps == null) { + mAttributedHistoricalOps = new ArrayMap<>(opCount); } - mHistoricalFeatureOps.put(cloneOps.getFeatureId(), cloneOps); + mAttributedHistoricalOps.put(cloneOps.getTag(), cloneOps); } } private HistoricalPackageOps(@NonNull Parcel parcel) { mPackageName = parcel.readString(); - mHistoricalFeatureOps = parcel.createTypedArrayMap(HistoricalFeatureOps.CREATOR); + mAttributedHistoricalOps = parcel.createTypedArrayMap(AttributedHistoricalOps.CREATOR); } private @Nullable HistoricalPackageOps splice(double fractionToRemove) { HistoricalPackageOps splice = null; - final int featureCount = getFeatureCount(); - for (int i = 0; i < featureCount; i++) { - final HistoricalFeatureOps origOps = getFeatureOpsAt(i); - final HistoricalFeatureOps spliceOps = origOps.splice(fractionToRemove); + final int attributionCount = getAttributedOpsCount(); + for (int i = 0; i < attributionCount; i++) { + final AttributedHistoricalOps origOps = getAttributedOpsAt(i); + final AttributedHistoricalOps spliceOps = origOps.splice(fractionToRemove); if (spliceOps != null) { if (splice == null) { splice = new HistoricalPackageOps(mPackageName); } - if (splice.mHistoricalFeatureOps == null) { - splice.mHistoricalFeatureOps = new ArrayMap<>(); + if (splice.mAttributedHistoricalOps == null) { + splice.mAttributedHistoricalOps = new ArrayMap<>(); } - splice.mHistoricalFeatureOps.put(spliceOps.getFeatureId(), spliceOps); + splice.mAttributedHistoricalOps.put(spliceOps.getTag(), spliceOps); } } return splice; } private void merge(@NonNull HistoricalPackageOps other) { - final int featureCount = other.getFeatureCount(); - for (int i = 0; i < featureCount; i++) { - final HistoricalFeatureOps otherFeatureOps = other.getFeatureOpsAt(i); - final HistoricalFeatureOps thisFeatureOps = getFeatureOps( - otherFeatureOps.getFeatureId()); - if (thisFeatureOps != null) { - thisFeatureOps.merge(otherFeatureOps); + final int attributionCount = other.getAttributedOpsCount(); + for (int i = 0; i < attributionCount; i++) { + final AttributedHistoricalOps otherAttributionOps = other.getAttributedOpsAt(i); + final AttributedHistoricalOps thisAttributionOps = getAttributedOps( + otherAttributionOps.getTag()); + if (thisAttributionOps != null) { + thisAttributionOps.merge(otherAttributionOps); } else { - if (mHistoricalFeatureOps == null) { - mHistoricalFeatureOps = new ArrayMap<>(); + if (mAttributedHistoricalOps == null) { + mAttributedHistoricalOps = new ArrayMap<>(); } - mHistoricalFeatureOps.put(otherFeatureOps.getFeatureId(), otherFeatureOps); + mAttributedHistoricalOps.put(otherAttributionOps.getTag(), + otherAttributionOps); } } } - private void filter(@Nullable String featureId, @Nullable String[] opNames, + private void filter(@Nullable String attributionTag, @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter, double fractionToRemove) { - final int featureCount = getFeatureCount(); - for (int i = featureCount - 1; i >= 0; i--) { - final HistoricalFeatureOps featureOps = getFeatureOpsAt(i); - if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(featureId, - featureOps.getFeatureId())) { - mHistoricalFeatureOps.removeAt(i); + final int attributionCount = getAttributedOpsCount(); + for (int i = attributionCount - 1; i >= 0; i--) { + final AttributedHistoricalOps attributionOps = getAttributedOpsAt(i); + if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(attributionTag, + attributionOps.getTag())) { + mAttributedHistoricalOps.removeAt(i); } else { - featureOps.filter(opNames, filter, fractionToRemove); - if (featureOps.getOpCount() == 0) { - mHistoricalFeatureOps.removeAt(i); + attributionOps.filter(opNames, filter, fractionToRemove); + if (attributionOps.getOpCount() == 0) { + mAttributedHistoricalOps.removeAt(i); } } } @@ -5180,38 +5214,38 @@ public class AppOpsManager { private void accept(@NonNull HistoricalOpsVisitor visitor) { visitor.visitHistoricalPackageOps(this); - final int featureCount = getFeatureCount(); - for (int i = 0; i < featureCount; i++) { - getFeatureOpsAt(i).accept(visitor); + final int attributionCount = getAttributedOpsCount(); + for (int i = 0; i < attributionCount; i++) { + getAttributedOpsAt(i).accept(visitor); } } private boolean isEmpty() { - final int featureCount = getFeatureCount(); - for (int i = featureCount - 1; i >= 0; i--) { - final HistoricalFeatureOps featureOps = mHistoricalFeatureOps.valueAt(i); - if (!featureOps.isEmpty()) { + final int attributionCount = getAttributedOpsCount(); + for (int i = attributionCount - 1; i >= 0; i--) { + final AttributedHistoricalOps attributionOps = mAttributedHistoricalOps.valueAt(i); + if (!attributionOps.isEmpty()) { return false; } } return true; } - private void increaseAccessCount(int opCode, @Nullable String featureId, + private void increaseAccessCount(int opCode, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long increment) { - getOrCreateHistoricalFeatureOps(featureId).increaseAccessCount( + getOrCreateAttributedHistoricalOps(attributionTag).increaseAccessCount( opCode, uidState, flags, increment); } - private void increaseRejectCount(int opCode, @Nullable String featureId, + private void increaseRejectCount(int opCode, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long increment) { - getOrCreateHistoricalFeatureOps(featureId).increaseRejectCount( + getOrCreateAttributedHistoricalOps(attributionTag).increaseRejectCount( opCode, uidState, flags, increment); } - private void increaseAccessDuration(int opCode, @Nullable String featureId, + private void increaseAccessDuration(int opCode, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long increment) { - getOrCreateHistoricalFeatureOps(featureId).increaseAccessDuration( + getOrCreateAttributedHistoricalOps(attributionTag).increaseAccessDuration( opCode, uidState, flags, increment); } @@ -5224,17 +5258,18 @@ public class AppOpsManager { return mPackageName; } - private @NonNull HistoricalFeatureOps getOrCreateHistoricalFeatureOps( - @Nullable String featureId) { - if (mHistoricalFeatureOps == null) { - mHistoricalFeatureOps = new ArrayMap<>(); + private @NonNull AttributedHistoricalOps getOrCreateAttributedHistoricalOps( + @Nullable String attributionTag) { + if (mAttributedHistoricalOps == null) { + mAttributedHistoricalOps = new ArrayMap<>(); } - HistoricalFeatureOps historicalFeatureOp = mHistoricalFeatureOps.get(featureId); - if (historicalFeatureOp == null) { - historicalFeatureOp = new HistoricalFeatureOps(featureId); - mHistoricalFeatureOps.put(featureId, historicalFeatureOp); + AttributedHistoricalOps historicalAttributionOp = mAttributedHistoricalOps.get( + attributionTag); + if (historicalAttributionOp == null) { + historicalAttributionOp = new AttributedHistoricalOps(attributionTag); + mAttributedHistoricalOps.put(attributionTag, historicalAttributionOp); } - return historicalFeatureOp; + return historicalAttributionOp; } /** @@ -5245,13 +5280,13 @@ public class AppOpsManager { */ public @IntRange(from = 0) int getOpCount() { int numOps = 0; - int numFeatures = getFeatureCount(); + int numAttributions = getAttributedOpsCount(); for (int code = 0; code < _NUM_OP; code++) { String opName = opToPublicName(code); - for (int featureNum = 0; featureNum < numFeatures; featureNum++) { - if (getFeatureOpsAt(featureNum).getOp(opName) != null) { + for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) { + if (getAttributedOpsAt(attributionNum).getOp(opName) != null) { numOps++; break; } @@ -5264,7 +5299,7 @@ public class AppOpsManager { /** * Gets the historical op at a given index. * - * <p>This combines the counts from all features. + * <p>This combines the counts from all attributions. * * @param index The index to lookup. * @return The op at the given index. @@ -5272,13 +5307,13 @@ public class AppOpsManager { */ public @NonNull HistoricalOp getOpAt(@IntRange(from = 0) int index) { int numOpsFound = 0; - int numFeatures = getFeatureCount(); + int numAttributions = getAttributedOpsCount(); for (int code = 0; code < _NUM_OP; code++) { String opName = opToPublicName(code); - for (int featureNum = 0; featureNum < numFeatures; featureNum++) { - if (getFeatureOpsAt(featureNum).getOp(opName) != null) { + for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) { + if (getAttributedOpsAt(attributionNum).getOp(opName) != null) { if (numOpsFound == index) { return getOp(opName); } else { @@ -5295,25 +5330,25 @@ public class AppOpsManager { /** * Gets the historical entry for a given op name. * - * <p>This combines the counts from all features. + * <p>This combines the counts from all attributions. * * @param opName The op name. * @return The historical entry for that op name. */ public @Nullable HistoricalOp getOp(@NonNull String opName) { - if (mHistoricalFeatureOps == null) { + if (mAttributedHistoricalOps == null) { return null; } HistoricalOp combinedOp = null; - int numFeatures = getFeatureCount(); - for (int i = 0; i < numFeatures; i++) { - HistoricalOp featureOp = getFeatureOpsAt(i).getOp(opName); - if (featureOp != null) { + int numAttributions = getAttributedOpsCount(); + for (int i = 0; i < numAttributions; i++) { + HistoricalOp attributionOp = getAttributedOpsAt(i).getOp(opName); + if (attributionOp != null) { if (combinedOp == null) { - combinedOp = new HistoricalOp(featureOp); + combinedOp = new HistoricalOp(attributionOp); } else { - combinedOp.merge(featureOp); + combinedOp.merge(attributionOp); } } } @@ -5329,7 +5364,7 @@ public class AppOpsManager { @Override public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeString(mPackageName); - parcel.writeTypedArrayMap(mHistoricalFeatureOps, flags); + parcel.writeTypedArrayMap(mAttributedHistoricalOps, flags); } public static final @android.annotation.NonNull Creator<HistoricalPackageOps> CREATOR = @@ -5357,11 +5392,11 @@ public class AppOpsManager { if (!mPackageName.equals(other.mPackageName)) { return false; } - if (mHistoricalFeatureOps == null) { - if (other.mHistoricalFeatureOps != null) { + if (mAttributedHistoricalOps == null) { + if (other.mAttributedHistoricalOps != null) { return false; } - } else if (!mHistoricalFeatureOps.equals(other.mHistoricalFeatureOps)) { + } else if (!mAttributedHistoricalOps.equals(other.mAttributedHistoricalOps)) { return false; } return true; @@ -5370,58 +5405,58 @@ public class AppOpsManager { @Override public int hashCode() { int result = mPackageName != null ? mPackageName.hashCode() : 0; - result = 31 * result + (mHistoricalFeatureOps != null ? mHistoricalFeatureOps.hashCode() - : 0); + result = 31 * result + (mAttributedHistoricalOps != null + ? mAttributedHistoricalOps.hashCode() : 0); return result; } /** - * Gets number of feature with historical ops. + * Gets number of attributed historical ops. * - * @return The number of feature with historical ops. + * @return The number of attribution with historical ops. * - * @see #getFeatureOpsAt(int) + * @see #getAttributedOpsAt(int) */ - public @IntRange(from = 0) int getFeatureCount() { - if (mHistoricalFeatureOps == null) { + public @IntRange(from = 0) int getAttributedOpsCount() { + if (mAttributedHistoricalOps == null) { return 0; } - return mHistoricalFeatureOps.size(); + return mAttributedHistoricalOps.size(); } /** - * Gets the historical feature ops at a given index. + * Gets the attributed historical ops at a given index. * * @param index The index. * - * @return The historical feature ops at the given index. + * @return The historical attribution ops at the given index. * - * @see #getFeatureCount() + * @see #getAttributedOpsCount() */ - public @NonNull HistoricalFeatureOps getFeatureOpsAt(@IntRange(from = 0) int index) { - if (mHistoricalFeatureOps == null) { + public @NonNull AttributedHistoricalOps getAttributedOpsAt(@IntRange(from = 0) int index) { + if (mAttributedHistoricalOps == null) { throw new IndexOutOfBoundsException(); } - return mHistoricalFeatureOps.valueAt(index); + return mAttributedHistoricalOps.valueAt(index); } /** - * Gets the historical feature ops for a given feature. + * Gets the attributed historical ops for a given attribution tag. * - * @param featureId The feature id. + * @param attributionTag The attribution tag. * - * @return The historical ops for the feature. + * @return The historical ops for the attribution. */ - public @Nullable HistoricalFeatureOps getFeatureOps(@NonNull String featureId) { - if (mHistoricalFeatureOps == null) { + public @Nullable AttributedHistoricalOps getAttributedOps(@NonNull String attributionTag) { + if (mAttributedHistoricalOps == null) { return null; } - return mHistoricalFeatureOps.get(featureId); + return mAttributedHistoricalOps.get(attributionTag); } } /** - * This class represents historical app op information about a feature in a package. + * This class represents historical app op information about a attribution in a package. * * @hide */ @@ -5431,20 +5466,20 @@ public class AppOpsManager { @DataClass(genHiddenConstructor = true, genEqualsHashCode = true, genHiddenCopyConstructor = true) */ @DataClass.Suppress("getHistoricalOps") - public static final class HistoricalFeatureOps implements Parcelable { - /** Id of the {@link Context#createFeatureContext feature} in the package */ - private final @Nullable String mFeatureId; + public static final class AttributedHistoricalOps implements Parcelable { + /** {@link Context#createAttributionContext attribution} tag */ + private final @Nullable String mTag; - /** Ops for this feature */ + /** Ops for this attribution */ private @Nullable ArrayMap<String, HistoricalOp> mHistoricalOps; /** @hide */ - public HistoricalFeatureOps(@NonNull String featureId) { - mFeatureId = featureId; + public AttributedHistoricalOps(@NonNull String tag) { + mTag = tag; } - private HistoricalFeatureOps(@NonNull HistoricalFeatureOps other) { - mFeatureId = other.mFeatureId; + private AttributedHistoricalOps(@NonNull AttributedHistoricalOps other) { + mTag = other.mTag; final int opCount = other.getOpCount(); for (int i = 0; i < opCount; i++) { final HistoricalOp origOp = other.getOpAt(i); @@ -5456,15 +5491,15 @@ public class AppOpsManager { } } - private @Nullable HistoricalFeatureOps splice(double fractionToRemove) { - HistoricalFeatureOps splice = null; + private @Nullable AttributedHistoricalOps splice(double fractionToRemove) { + AttributedHistoricalOps splice = null; final int opCount = getOpCount(); for (int i = 0; i < opCount; i++) { final HistoricalOp origOps = getOpAt(i); final HistoricalOp spliceOps = origOps.splice(fractionToRemove); if (spliceOps != null) { if (splice == null) { - splice = new HistoricalFeatureOps(mFeatureId, null); + splice = new AttributedHistoricalOps(mTag, null); } if (splice.mHistoricalOps == null) { splice.mHistoricalOps = new ArrayMap<>(); @@ -5475,7 +5510,7 @@ public class AppOpsManager { return splice; } - private void merge(@NonNull HistoricalFeatureOps other) { + private void merge(@NonNull AttributedHistoricalOps other) { final int opCount = other.getOpCount(); for (int i = 0; i < opCount; i++) { final HistoricalOp otherOp = other.getOpAt(i); @@ -5572,7 +5607,7 @@ public class AppOpsManager { } private void accept(@NonNull HistoricalOpsVisitor visitor) { - visitor.visitHistoricalFeatureOps(this); + visitor.visitHistoricalAttributionOps(this); final int opCount = getOpCount(); for (int i = 0; i < opCount; i++) { getOpAt(i).accept(visitor); @@ -5608,46 +5643,46 @@ public class AppOpsManager { /** - * Creates a new HistoricalFeatureOps. + * Creates a new HistoricalAttributionOps. * - * @param featureId - * Id of the {@link Context#createFeatureContext feature} in the package + * @param tag + * {@link Context#createAttributionContext attribution} tag * @param historicalOps - * Ops for this feature + * Ops for this attribution * @hide */ @DataClass.Generated.Member - public HistoricalFeatureOps( - @Nullable String featureId, + public AttributedHistoricalOps( + @Nullable String tag, @Nullable ArrayMap<String,HistoricalOp> historicalOps) { - this.mFeatureId = featureId; + this.mTag = tag; this.mHistoricalOps = historicalOps; // onConstructed(); // You can define this method to get a callback } /** - * Id of the {@link Context#createFeatureContext feature} in the package + * {@link Context#createAttributionContext attribution} tag */ @DataClass.Generated.Member - public @Nullable String getFeatureId() { - return mFeatureId; + public @Nullable String getTag() { + return mTag; } @Override @DataClass.Generated.Member public boolean equals(@Nullable Object o) { // You can override field equality logic by defining either of the methods like: - // boolean fieldNameEquals(HistoricalFeatureOps other) { ... } + // boolean fieldNameEquals(HistoricalAttributionOps other) { ... } // boolean fieldNameEquals(FieldType otherValue) { ... } if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @SuppressWarnings("unchecked") - HistoricalFeatureOps that = (HistoricalFeatureOps) o; + AttributedHistoricalOps that = (AttributedHistoricalOps) o; //noinspection PointlessBooleanExpression return true - && Objects.equals(mFeatureId, that.mFeatureId) + && Objects.equals(mTag, that.mTag) && Objects.equals(mHistoricalOps, that.mHistoricalOps); } @@ -5658,7 +5693,7 @@ public class AppOpsManager { // int fieldNameHashCode() { ... } int _hash = 1; - _hash = 31 * _hash + Objects.hashCode(mFeatureId); + _hash = 31 * _hash + Objects.hashCode(mTag); _hash = 31 * _hash + Objects.hashCode(mHistoricalOps); return _hash; } @@ -5670,10 +5705,10 @@ public class AppOpsManager { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mFeatureId != null) flg |= 0x1; + if (mTag != null) flg |= 0x1; if (mHistoricalOps != null) flg |= 0x2; dest.writeByte(flg); - if (mFeatureId != null) dest.writeString(mFeatureId); + if (mTag != null) dest.writeString(mTag); if (mHistoricalOps != null) dest.writeMap(mHistoricalOps); } @@ -5684,35 +5719,35 @@ public class AppOpsManager { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ HistoricalFeatureOps(@NonNull Parcel in) { + /* package-private */ AttributedHistoricalOps(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } byte flg = in.readByte(); - String featureId = (flg & 0x1) == 0 ? null : in.readString(); + String attributionTag = (flg & 0x1) == 0 ? null : in.readString(); ArrayMap<String,HistoricalOp> historicalOps = null; if ((flg & 0x2) != 0) { historicalOps = new ArrayMap(); in.readMap(historicalOps, HistoricalOp.class.getClassLoader()); } - this.mFeatureId = featureId; + this.mTag = attributionTag; this.mHistoricalOps = historicalOps; // onConstructed(); // You can define this method to get a callback } @DataClass.Generated.Member - public static final @NonNull Parcelable.Creator<HistoricalFeatureOps> CREATOR - = new Parcelable.Creator<HistoricalFeatureOps>() { + public static final @NonNull Parcelable.Creator<AttributedHistoricalOps> CREATOR + = new Parcelable.Creator<AttributedHistoricalOps>() { @Override - public HistoricalFeatureOps[] newArray(int size) { - return new HistoricalFeatureOps[size]; + public AttributedHistoricalOps[] newArray(int size) { + return new AttributedHistoricalOps[size]; } @Override - public HistoricalFeatureOps createFromParcel(@NonNull Parcel in) { - return new HistoricalFeatureOps(in); + public AttributedHistoricalOps createFromParcel(@NonNull Parcel in) { + return new AttributedHistoricalOps(in); } }; @@ -5721,7 +5756,7 @@ public class AppOpsManager { time = 1578113234821L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java", - inputSignatures = "private final @android.annotation.Nullable java.lang.String mFeatureId\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.app.HistoricalOp> mHistoricalOps\nprivate @android.annotation.Nullable android.app.HistoricalFeatureOps splice(double)\nprivate void merge(android.app.HistoricalFeatureOps)\nprivate void filter(java.lang.String[],int,double)\nprivate boolean isEmpty()\nprivate void increaseAccessCount(int,int,int,long)\nprivate void increaseRejectCount(int,int,int,long)\nprivate void increaseAccessDuration(int,int,int,long)\npublic @android.annotation.IntRange(from=0L) int getOpCount()\npublic @android.annotation.NonNull android.app.HistoricalOp getOpAt(int)\npublic @android.annotation.Nullable android.app.HistoricalOp getOp(java.lang.String)\nprivate void accept(android.app.HistoricalOpsVisitor)\nprivate @android.annotation.NonNull android.app.HistoricalOp getOrCreateHistoricalOp(int)\nclass HistoricalFeatureOps extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genHiddenCopyConstructor=true)") + inputSignatures = "private final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.app.HistoricalOp> mHistoricalOps\nprivate @android.annotation.Nullable android.app.HistoricalAttributionOps splice(double)\nprivate void merge(android.app.HistoricalAttributionOps)\nprivate void filter(java.lang.String[],int,double)\nprivate boolean isEmpty()\nprivate void increaseAccessCount(int,int,int,long)\nprivate void increaseRejectCount(int,int,int,long)\nprivate void increaseAccessDuration(int,int,int,long)\npublic @android.annotation.IntRange(from=0L) int getOpCount()\npublic @android.annotation.NonNull android.app.HistoricalOp getOpAt(int)\npublic @android.annotation.Nullable android.app.HistoricalOp getOp(java.lang.String)\nprivate void accept(android.app.HistoricalOpsVisitor)\nprivate @android.annotation.NonNull android.app.HistoricalOp getOrCreateHistoricalOp(int)\nclass HistoricalAttributionOps extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genHiddenCopyConstructor=true)") @Deprecated private void __metadata() {} */ @@ -6397,7 +6432,7 @@ public class AppOpsManager { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(callback, "callback cannot be null"); try { - mService.getHistoricalOps(request.mUid, request.mPackageName, request.mFeatureId, + mService.getHistoricalOps(request.mUid, request.mPackageName, request.mAttributionTag, request.mOpNames, request.mFilter, request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> { final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS); @@ -6437,8 +6472,9 @@ public class AppOpsManager { Objects.requireNonNull(callback, "callback cannot be null"); try { mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName, - request.mFeatureId, request.mOpNames, request.mFilter, request.mBeginTimeMillis, - request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> { + request.mAttributionTag, request.mOpNames, request.mFilter, + request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags, + new RemoteCallback((result) -> { final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS); final long identity = Binder.clearCallingIdentity(); try { @@ -6689,6 +6725,13 @@ public class AppOpsManager { }; mModeWatchers.put(callback, cb); } + + // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE + if (!Compatibility.isChangeEnabled( + CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE)) { + flags |= CALL_BACK_ON_SWITCHED_OP; + } + try { mService.startWatchingModeWithFlags(op, packageName, flags, cb); } catch (RemoteException e) { @@ -7021,8 +7064,8 @@ public class AppOpsManager { * @param op The operation to note. One of the OPSTR_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. - * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code - * null} for default feature + * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code + * null} for default attribution * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -7032,8 +7075,8 @@ public class AppOpsManager { * @throws SecurityException If the app has been configured to crash on this op. */ public int noteOp(@NonNull String op, int uid, @Nullable String packageName, - @Nullable String featureId, @Nullable String message) { - return noteOp(strOpToOp(op), uid, packageName, featureId, message); + @Nullable String attributionTag, @Nullable String message) { + return noteOp(strOpToOp(op), uid, packageName, attributionTag, message); } /** @@ -7049,7 +7092,8 @@ public class AppOpsManager { * @param op The operation to note. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. - * @param featureId The feature in the app or {@code null} for default feature + * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code + * null} for default attribution * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -7060,9 +7104,9 @@ public class AppOpsManager { * * @hide */ - public int noteOp(int op, int uid, @Nullable String packageName, @Nullable String featureId, - @Nullable String message) { - final int mode = noteOpNoThrow(op, uid, packageName, featureId, message); + public int noteOp(int op, int uid, @Nullable String packageName, + @Nullable String attributionTag, @Nullable String message) { + final int mode = noteOpNoThrow(op, uid, packageName, attributionTag, message); if (mode == MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } @@ -7097,8 +7141,8 @@ public class AppOpsManager { * @param op The operation to note. One of the OPSTR_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. - * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code - * null} for default feature + * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code + * null} for default attribution * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -7106,8 +7150,8 @@ public class AppOpsManager { * causing the app to crash). */ public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName, - @Nullable String featureId, @Nullable String message) { - return noteOpNoThrow(strOpToOp(op), uid, packageName, featureId, message); + @Nullable String attributionTag, @Nullable String message) { + return noteOpNoThrow(strOpToOp(op), uid, packageName, attributionTag, message); } /** @@ -7117,7 +7161,8 @@ public class AppOpsManager { * @param op The operation to note. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. - * @param featureId The feature in the app or {@code null} for default feature + * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code + * null} for default attribution * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -7127,7 +7172,7 @@ public class AppOpsManager { * @hide */ public int noteOpNoThrow(int op, int uid, @Nullable String packageName, - @Nullable String featureId, @Nullable String message) { + @Nullable String attributionTag, @Nullable String message) { try { collectNoteOpCallsForValidation(op); int collectionMode = getNotedOpCollectionMode(uid, packageName, op); @@ -7138,14 +7183,14 @@ public class AppOpsManager { } } - int mode = mService.noteOperation(op, uid, packageName, featureId, + int mode = mService.noteOperation(op, uid, packageName, attributionTag, collectionMode == COLLECT_ASYNC, message); if (mode == MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { - collectNotedOpForSelf(op, featureId); + collectNotedOpForSelf(op, attributionTag); } else if (collectionMode == COLLECT_SYNC) { - collectNotedOpSync(op, featureId); + collectNotedOpSync(op, attributionTag); } } @@ -7185,8 +7230,8 @@ public class AppOpsManager { * @param op The operation to note. One of the OP_* constants. * @param proxiedPackageName The name of the application calling into the proxy application. * @param proxiedUid The uid of the proxied application - * @param proxiedFeatureId The feature in the proxied app or {@code null} for default - * feature + * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext + * attribution tag} or {@code null} for default attribution * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED} @@ -7198,8 +7243,8 @@ public class AppOpsManager { * @hide */ public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid, - @Nullable String proxiedFeatureId, @Nullable String message) { - int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, proxiedFeatureId, + @Nullable String proxiedAttributionTag, @Nullable String message) { + int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, proxiedAttributionTag, message); if (mode == MODE_ERRORED) { throw new SecurityException("Proxy package " + mContext.getOpPackageName() @@ -7218,8 +7263,8 @@ public class AppOpsManager { * @param op The operation to note. One of the OPSTR_* constants. * @param proxiedPackageName The name of the application calling into the proxy application. * @param proxiedUid The uid of the proxied application - * @param proxiedFeatureId The feature in the proxied app or {@code null} for default - * feature + * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext + * attribution tag} or {@code null} for default attribution * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED} @@ -7229,8 +7274,8 @@ public class AppOpsManager { * op. */ public int noteProxyOp(@NonNull String op, @Nullable String proxiedPackageName, int proxiedUid, - @Nullable String proxiedFeatureId, @Nullable String message) { - return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, proxiedFeatureId, + @Nullable String proxiedAttributionTag, @Nullable String message) { + return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, proxiedAttributionTag, message); } @@ -7261,14 +7306,14 @@ public class AppOpsManager { * @param op The op to note * @param proxiedPackageName The package to note the op for * @param proxiedUid The uid the package belongs to - * @param proxiedFeatureId The feature in the proxied app or {@code null} for default - * feature + * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext + * attribution tag} or {@code null} for default attribution * @param message A message describing the reason the op was noted */ public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName, - int proxiedUid, @Nullable String proxiedFeatureId, @Nullable String message) { + int proxiedUid, @Nullable String proxiedAttributionTag, @Nullable String message) { return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid, - proxiedFeatureId, message); + proxiedAttributionTag, message); } /** @@ -7279,14 +7324,14 @@ public class AppOpsManager { * @param proxiedPackageName The package to note the op for or {@code null} if the op should be * noted for the "android" package * @param proxiedUid The uid the package belongs to - * @param proxiedFeatureId The feature in the proxied app or {@code null} for default - * feature + * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext + * attribution tag} or {@code null} for default attribution * @param message A message describing the reason the op was noted * * @hide */ public int noteProxyOpNoThrow(int op, @Nullable String proxiedPackageName, int proxiedUid, - @Nullable String proxiedFeatureId, @Nullable String message) { + @Nullable String proxiedAttributionTag, @Nullable String message) { int myUid = Process.myUid(); try { @@ -7300,17 +7345,17 @@ public class AppOpsManager { } int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName, - proxiedFeatureId, myUid, mContext.getOpPackageName(), - mContext.getFeatureId(), collectionMode == COLLECT_ASYNC, message); + proxiedAttributionTag, myUid, mContext.getOpPackageName(), + mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message); if (mode == MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { - collectNotedOpForSelf(op, proxiedFeatureId); + collectNotedOpForSelf(op, proxiedAttributionTag); } else if (collectionMode == COLLECT_SYNC // Only collect app-ops when the proxy is trusted && mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, myUid) == PackageManager.PERMISSION_GRANTED) { - collectNotedOpSync(op, proxiedFeatureId); + collectNotedOpSync(op, proxiedAttributionTag); } } @@ -7499,8 +7544,8 @@ public class AppOpsManager { * @param op The operation to start. One of the OPSTR_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. - * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code - * null} for default feature + * @param attributionTag The {@link Context#createAttributionContext attribution tag} or + * {@code null} for default attribution * @param message Description why op was started * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -7511,8 +7556,8 @@ public class AppOpsManager { * the package is not in the passed in UID. */ public int startOp(@NonNull String op, int uid, @Nullable String packageName, - @Nullable String featureId, @Nullable String message) { - return startOp(strOpToOp(op), uid, packageName, false, featureId, message); + @Nullable String attributionTag, @Nullable String message) { + return startOp(strOpToOp(op), uid, packageName, false, attributionTag, message); } /** @@ -7521,7 +7566,8 @@ public class AppOpsManager { * @param op The operation to start. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. - * @param featureId The feature in the app or {@code null} for default feature + * @param attributionTag The {@link Context#createAttributionContext attribution tag} or + * {@code null} for default attribution * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}. * @param message Description why op was started * @@ -7535,8 +7581,8 @@ public class AppOpsManager { * @hide */ public int startOp(int op, int uid, @Nullable String packageName, boolean startIfModeDefault, - @Nullable String featureId, @Nullable String message) { - final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, featureId, + @Nullable String attributionTag, @Nullable String message) { + final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, attributionTag, message); if (mode == MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); @@ -7579,7 +7625,8 @@ public class AppOpsManager { * @param op The operation to start. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. - * @param featureId The feature in the app or {@code null} for default feature + * @param attributionTag The {@link Context#createAttributionContext attribution tag} or + * {@code null} for default attribution * @param message Description why op was started * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -7587,8 +7634,8 @@ public class AppOpsManager { * causing the app to crash). */ public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName, - @NonNull String featureId, @Nullable String message) { - return startOpNoThrow(strOpToOp(op), uid, packageName, false, featureId, message); + @NonNull String attributionTag, @Nullable String message) { + return startOpNoThrow(strOpToOp(op), uid, packageName, false, attributionTag, message); } /** @@ -7598,7 +7645,8 @@ public class AppOpsManager { * @param op The operation to start. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. - * @param featureId The feature in the app or {@code null} for default feature + * @param attributionTag The {@link Context#createAttributionContext attribution tag} or + * {@code null} for default attribution * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}. * @param message Description why op was started * @@ -7609,7 +7657,7 @@ public class AppOpsManager { * @hide */ public int startOpNoThrow(int op, int uid, @NonNull String packageName, - boolean startIfModeDefault, @Nullable String featureId, @Nullable String message) { + boolean startIfModeDefault, @Nullable String attributionTag, @Nullable String message) { try { collectNoteOpCallsForValidation(op); int collectionMode = getNotedOpCollectionMode(uid, packageName, op); @@ -7621,13 +7669,13 @@ public class AppOpsManager { } int mode = mService.startOperation(getClientId(), op, uid, packageName, - featureId, startIfModeDefault, collectionMode == COLLECT_ASYNC, message); + attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message); if (mode == MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { - collectNotedOpForSelf(op, featureId); + collectNotedOpForSelf(op, attributionTag); } else if (collectionMode == COLLECT_SYNC) { - collectNotedOpSync(op, featureId); + collectNotedOpSync(op, attributionTag); } } @@ -7661,8 +7709,8 @@ public class AppOpsManager { * previously passed in when starting the operation. */ public void finishOp(@NonNull String op, int uid, @NonNull String packageName, - @Nullable String featureId) { - finishOp(strOpToOp(op), uid, packageName, featureId); + @Nullable String attributionTag) { + finishOp(strOpToOp(op), uid, packageName, attributionTag); } /** @@ -7683,9 +7731,9 @@ public class AppOpsManager { * @hide */ public void finishOp(int op, int uid, @NonNull String packageName, - @Nullable String featureId) { + @Nullable String attributionTag) { try { - mService.finishOperation(getClientId(), op, uid, packageName, featureId); + mService.finishOperation(getClientId(), op, uid, packageName, attributionTag); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -7796,15 +7844,15 @@ public class AppOpsManager { * Collect a noted op for the current process. * * @param op The noted op - * @param featureId The feature the op is noted for + * @param attributionTag The attribution tag the op is noted for */ - private void collectNotedOpForSelf(int op, @Nullable String featureId) { + private void collectNotedOpForSelf(int op, @Nullable String attributionTag) { synchronized (sLock) { if (sOnOpNotedCallback != null) { - sOnOpNotedCallback.onSelfNoted(new SyncNotedAppOp(op, featureId)); + sOnOpNotedCallback.onSelfNoted(new SyncNotedAppOp(op, attributionTag)); } } - sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, featureId)); + sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, attributionTag)); } /** @@ -7813,9 +7861,9 @@ public class AppOpsManager { * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded} * * @param op The noted op - * @param featureId The feature the op is noted for + * @param attributionTag The attribution tag the op is noted for */ - private void collectNotedOpSync(int op, @Nullable String featureId) { + private void collectNotedOpSync(int op, @Nullable String attributionTag) { // If this is inside of a two-way binder call: // We are inside of a two-way binder call. Delivered to caller via // {@link #prefixParcelWithAppOpsIfNeeded} @@ -7825,16 +7873,16 @@ public class AppOpsManager { sAppOpsNotedInThisBinderTransaction.set(appOpsNoted); } - long[] appOpsNotedForFeature = appOpsNoted.get(featureId); - if (appOpsNotedForFeature == null) { - appOpsNotedForFeature = new long[2]; - appOpsNoted.put(featureId, appOpsNotedForFeature); + long[] appOpsNotedForAttribution = appOpsNoted.get(attributionTag); + if (appOpsNotedForAttribution == null) { + appOpsNotedForAttribution = new long[2]; + appOpsNoted.put(attributionTag, appOpsNotedForAttribution); } if (op < 64) { - appOpsNotedForFeature[0] |= 1L << op; + appOpsNotedForAttribution[0] |= 1L << op; } else { - appOpsNotedForFeature[1] |= 1L << (op - 64); + appOpsNotedForAttribution[1] |= 1L << (op - 64); } } @@ -7915,10 +7963,10 @@ public class AppOpsManager { p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER); - int numFeatureWithNotesAppOps = notedAppOps.size(); - p.writeInt(numFeatureWithNotesAppOps); + int numAttributionWithNotesAppOps = notedAppOps.size(); + p.writeInt(numAttributionWithNotesAppOps); - for (int i = 0; i < numFeatureWithNotesAppOps; i++) { + for (int i = 0; i < numAttributionWithNotesAppOps; i++) { p.writeString(notedAppOps.keyAt(i)); p.writeLong(notedAppOps.valueAt(i)[0]); p.writeLong(notedAppOps.valueAt(i)[1]); @@ -7936,10 +7984,10 @@ public class AppOpsManager { * @hide */ public static void readAndLogNotedAppops(@NonNull Parcel p) { - int numFeaturesWithNotedAppOps = p.readInt(); + int numAttributionsWithNotedAppOps = p.readInt(); - for (int i = 0; i < numFeaturesWithNotedAppOps; i++) { - String featureId = p.readString(); + for (int i = 0; i < numAttributionsWithNotedAppOps; i++) { + String attributionTag = p.readString(); long[] rawNotedAppOps = new long[2]; rawNotedAppOps[0] = p.readLong(); rawNotedAppOps[1] = p.readLong(); @@ -7951,13 +7999,13 @@ public class AppOpsManager { for (int code = notedAppOps.nextSetBit(0); code != -1; code = notedAppOps.nextSetBit(code + 1)) { if (sOnOpNotedCallback != null) { - sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, featureId)); + sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag)); } } } for (int code = notedAppOps.nextSetBit(0); code != -1; code = notedAppOps.nextSetBit(code + 1)) { - sMessageCollector.onNoted(new SyncNotedAppOp(code, featureId)); + sMessageCollector.onNoted(new SyncNotedAppOp(code, attributionTag)); } } } diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java index 4d955dbe8703..b0c2762c3439 100644 --- a/core/java/android/app/AsyncNotedAppOp.java +++ b/core/java/android/app/AsyncNotedAppOp.java @@ -48,8 +48,8 @@ public final class AsyncNotedAppOp implements Parcelable { /** Uid that noted the op */ private final @IntRange(from = 0) int mNotingUid; - /** {@link android.content.Context#createFeatureContext Feature} in the app */ - private final @Nullable String mFeatureId; + /** {@link android.content.Context#createAttributionContext attribution tag} */ + private final @Nullable String mAttributionTag; /** Message associated with the noteOp. This message is set by the app noting the op */ private final @NonNull String mMessage; @@ -92,8 +92,8 @@ public final class AsyncNotedAppOp implements Parcelable { * Op that was noted * @param notingUid * Uid that noted the op - * @param featureId - * {@link android.content.Context#createFeatureContext Feature} in the app + * @param attributionTag + * {@link android.content.Context#createAttributionContext attribution tag} * @param message * Message associated with the noteOp. This message is set by the app noting the op * @param time @@ -104,7 +104,7 @@ public final class AsyncNotedAppOp implements Parcelable { public AsyncNotedAppOp( @IntRange(from = 0) int opCode, @IntRange(from = 0) int notingUid, - @Nullable String featureId, + @Nullable String attributionTag, @NonNull String message, @CurrentTimeMillisLong long time) { this.mOpCode = opCode; @@ -115,7 +115,7 @@ public final class AsyncNotedAppOp implements Parcelable { com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mNotingUid, "from", 0); - this.mFeatureId = featureId; + this.mAttributionTag = attributionTag; this.mMessage = message; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mMessage); @@ -135,11 +135,11 @@ public final class AsyncNotedAppOp implements Parcelable { } /** - * {@link android.content.Context#createFeatureContext Feature} in the app + * {@link android.content.Context#createAttributionContext attribution tag} */ @DataClass.Generated.Member - public @Nullable String getFeatureId() { - return mFeatureId; + public @Nullable String getAttributionTag() { + return mAttributionTag; } /** @@ -173,7 +173,7 @@ public final class AsyncNotedAppOp implements Parcelable { return true && mOpCode == that.mOpCode && mNotingUid == that.mNotingUid - && java.util.Objects.equals(mFeatureId, that.mFeatureId) + && java.util.Objects.equals(mAttributionTag, that.mAttributionTag) && java.util.Objects.equals(mMessage, that.mMessage) && mTime == that.mTime; } @@ -187,7 +187,7 @@ public final class AsyncNotedAppOp implements Parcelable { int _hash = 1; _hash = 31 * _hash + mOpCode; _hash = 31 * _hash + mNotingUid; - _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId); + _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag); _hash = 31 * _hash + java.util.Objects.hashCode(mMessage); _hash = 31 * _hash + Long.hashCode(mTime); return _hash; @@ -200,11 +200,11 @@ public final class AsyncNotedAppOp implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mFeatureId != null) flg |= 0x4; + if (mAttributionTag != null) flg |= 0x4; dest.writeByte(flg); dest.writeInt(mOpCode); dest.writeInt(mNotingUid); - if (mFeatureId != null) dest.writeString(mFeatureId); + if (mAttributionTag != null) dest.writeString(mAttributionTag); dest.writeString(mMessage); dest.writeLong(mTime); } @@ -223,7 +223,7 @@ public final class AsyncNotedAppOp implements Parcelable { byte flg = in.readByte(); int opCode = in.readInt(); int notingUid = in.readInt(); - String featureId = (flg & 0x4) == 0 ? null : in.readString(); + String attributionTag = (flg & 0x4) == 0 ? null : in.readString(); String message = in.readString(); long time = in.readLong(); @@ -235,7 +235,7 @@ public final class AsyncNotedAppOp implements Parcelable { com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mNotingUid, "from", 0); - this.mFeatureId = featureId; + this.mAttributionTag = attributionTag; this.mMessage = message; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mMessage); @@ -261,10 +261,10 @@ public final class AsyncNotedAppOp implements Parcelable { }; @DataClass.Generated( - time = 1583866178330L, + time = 1583866239013L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java", - inputSignatures = "private final @android.annotation.IntRange(from=0L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.CurrentTimeMillisLong long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nprivate void onConstructed()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.IntRange(from=0L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.CurrentTimeMillisLong long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nprivate void onConstructed()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 2873b10e60c8..56e6aee83a44 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -219,8 +219,8 @@ class ContextImpl extends Context { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final String mOpPackageName; - /** If of feature this context is for */ - private final @Nullable String mFeatureId; + /** Attribution tag of this context */ + private final @Nullable String mAttributionTag; private final @NonNull ResourcesManager mResourcesManager; @UnsupportedAppUsage @@ -421,8 +421,8 @@ class ContextImpl extends Context { /** @hide */ @Override - public @Nullable String getFeatureId() { - return mFeatureId; + public @Nullable String getAttributionTag() { + return mAttributionTag; } @Override @@ -1026,10 +1026,10 @@ class ContextImpl extends Context { public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) { try { ActivityTaskManager.getService().startActivityAsUser( - mMainThread.getApplicationThread(), getBasePackageName(), getFeatureId(), intent, - intent.resolveTypeIfNeeded(getContentResolver()), - null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, - user.getIdentifier()); + mMainThread.getApplicationThread(), getBasePackageName(), getAttributionTag(), + intent, intent.resolveTypeIfNeeded(getContentResolver()), + null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, + user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1109,9 +1109,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false, - getUserId()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, + false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1126,9 +1126,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, - null, false, false, getUserId()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, receiverPermissions, + AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1141,9 +1141,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, - null, false, false, getUserId()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, receiverPermissions, + AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1156,9 +1156,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, - null, false, false, user.getIdentifier()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, receiverPermissions, + AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1173,9 +1173,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, - options, false, false, getUserId()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, receiverPermissions, + AppOpsManager.OP_NONE, options, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1190,9 +1190,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false, - getUserId()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, + false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1207,9 +1207,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, - null, true, false, getUserId()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, receiverPermissions, + AppOpsManager.OP_NONE, null, true, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1270,8 +1270,8 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd, - initialCode, initialData, initialExtras, receiverPermissions, appOp, + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + rd, initialCode, initialData, initialExtras, receiverPermissions, appOp, options, true, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1284,9 +1284,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false, - user.getIdentifier()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, + false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1307,9 +1307,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, - options, false, false, user.getIdentifier()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, receiverPermissions, + AppOpsManager.OP_NONE, options, false, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1324,9 +1324,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false, - user.getIdentifier()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, + false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1375,9 +1375,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd, - initialCode, initialData, initialExtras, receiverPermissions, - appOp, options, true, false, user.getIdentifier()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + rd, initialCode, initialData, initialExtras, receiverPermissions, appOp, + options, true, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1416,9 +1416,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, - getUserId()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, + true, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1452,9 +1452,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd, - initialCode, initialData, initialExtras, null, - AppOpsManager.OP_NONE, null, true, true, getUserId()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + rd, initialCode, initialData, initialExtras, null, AppOpsManager.OP_NONE, null, + true, true, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1484,9 +1484,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, - user.getIdentifier()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, + true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1499,9 +1499,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true, - user.getIdentifier()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, + false, true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1534,9 +1534,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( - mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd, - initialCode, initialData, initialExtras, null, - AppOpsManager.OP_NONE, null, true, true, user.getIdentifier()); + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + rd, initialCode, initialData, initialExtras, null, AppOpsManager.OP_NONE, null, + true, true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1620,7 +1620,7 @@ class ContextImpl extends Context { } try { final Intent intent = ActivityManager.getService().registerReceiverWithFeature( - mMainThread.getApplicationThread(), mBasePackageName, getFeatureId(), rd, + mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd, filter, broadcastPermission, userId, flags); if (intent != null) { intent.setExtrasClassLoader(getClassLoader()); @@ -1696,7 +1696,7 @@ class ContextImpl extends Context { ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), requireForeground, - getOpPackageName(), getFeatureId(), user.getIdentifier()); + getOpPackageName(), getAttributionTag(), user.getIdentifier()); if (cn != null) { if (cn.getPackageName().equals("!")) { throw new SecurityException( @@ -2285,14 +2285,14 @@ class ContextImpl extends Context { if (packageName.equals("system") || packageName.equals("android")) { // The system resources are loaded in every application, so we can safely copy // the context without reloading Resources. - return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, null, + return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, null, mToken, user, flags, null, null); } LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); if (pi != null) { - ContextImpl c = new ContextImpl(this, mMainThread, pi, mFeatureId, null, + ContextImpl c = new ContextImpl(this, mMainThread, pi, mAttributionTag, null, mToken, user, flags, null, null); final int displayId = getDisplayId(); @@ -2329,7 +2329,7 @@ class ContextImpl extends Context { final String[] paths = mPackageInfo.getSplitPaths(splitName); final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, - mFeatureId, splitName, mToken, mUser, mFlags, classLoader, null); + mAttributionTag, splitName, mToken, mUser, mFlags, classLoader, null); final int displayId = getDisplayId(); @@ -2353,7 +2353,7 @@ class ContextImpl extends Context { throw new IllegalArgumentException("overrideConfiguration must not be null"); } - ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, + ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName, mToken, mUser, mFlags, mClassLoader, null); final int displayId = getDisplayId(); @@ -2370,7 +2370,7 @@ class ContextImpl extends Context { throw new IllegalArgumentException("display must not be null"); } - ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, + ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName, mToken, mUser, mFlags, mClassLoader, null); final int displayId = display.getDisplayId(); @@ -2394,7 +2394,7 @@ class ContextImpl extends Context { } ContextImpl createBaseWindowContext(IBinder token) { - ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, + ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName, token, mUser, mFlags, mClassLoader, null); context.mIsUiContext = true; @@ -2420,8 +2420,8 @@ class ContextImpl extends Context { } @Override - public @NonNull Context createFeatureContext(@Nullable String featureId) { - return new ContextImpl(this, mMainThread, mPackageInfo, featureId, mSplitName, + public @NonNull Context createAttributionContext(@Nullable String attributionTag) { + return new ContextImpl(this, mMainThread, mPackageInfo, attributionTag, mSplitName, mToken, mUser, mFlags, mClassLoader, null); } @@ -2429,7 +2429,7 @@ class ContextImpl extends Context { public Context createDeviceProtectedStorageContext() { final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE) | Context.CONTEXT_DEVICE_PROTECTED_STORAGE; - return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName, + return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName, mToken, mUser, flags, mClassLoader, null); } @@ -2437,7 +2437,7 @@ class ContextImpl extends Context { public Context createCredentialProtectedStorageContext() { final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE) | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE; - return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName, + return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName, mToken, mUser, flags, mClassLoader, null); } @@ -2700,7 +2700,7 @@ class ContextImpl extends Context { } private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread, - @NonNull LoadedApk packageInfo, @Nullable String featureId, + @NonNull LoadedApk packageInfo, @Nullable String attributionTag, @Nullable String splitName, @Nullable IBinder activityToken, @Nullable UserHandle user, int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) { mOuterContext = this; @@ -2754,7 +2754,7 @@ class ContextImpl extends Context { } mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName; - mFeatureId = featureId; + mAttributionTag = attributionTag; mContentResolver = new ApplicationContentResolver(this, mainThread); } diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 28b28dad5b82..145d5139cdc2 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -37,13 +37,15 @@ oneway interface ITaskStackListener { /** * Called whenever IActivityManager.startActivity is called on an activity that is already - * running in the pinned stack and the activity is not actually started, but the task is either - * brought to the front or a new Intent is delivered to it. + * running, but the task is either brought to the front or a new Intent is delivered to it. * + * @param task information about the task the activity was relaunched into + * @param homeVisible whether or not the home task is visible * @param clearedTask whether or not the launch activity also cleared the task as a part of * starting */ - void onPinnedActivityRestartAttempt(boolean clearedTask); + void onActivityRestartAttempt(in ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask); /** * Called when we launched an activity that we forced to be resizable. diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 34684c4c4228..4cb8d936aa9c 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -175,11 +175,4 @@ interface IWallpaperManager { * Called from SystemUI when it shows the AoD UI. */ oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration); - - /** - * Called when the wallpaper needs to zoom out. - * The zoom value goes from 0 to 1 (inclusive) where 1 means fully zoomed out, - * 0 means fully zoomed in - */ - oneway void setWallpaperZoomOut(float zoom, String callingPackage, int displayId); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 18932c6b0784..818a12145932 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1720,11 +1720,10 @@ public class Instrumentation { try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(who); - int result = ActivityTaskManager.getService() - .startActivity(whoThread, who.getBasePackageName(), who.getFeatureId(), intent, - intent.resolveTypeIfNeeded(who.getContentResolver()), - token, target != null ? target.mEmbeddedID : null, - requestCode, 0, null, options); + int result = ActivityTaskManager.getService().startActivity(whoThread, + who.getBasePackageName(), who.getAttributionTag(), intent, + intent.resolveTypeIfNeeded(who.getContentResolver()), token, + target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); @@ -1793,9 +1792,9 @@ public class Instrumentation { intents[i].prepareToLeaveProcess(who); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver()); } - int result = ActivityTaskManager.getService() - .startActivities(whoThread, who.getBasePackageName(), who.getFeatureId(), intents, - resolvedTypes, token, options, userId); + int result = ActivityTaskManager.getService().startActivities(whoThread, + who.getBasePackageName(), who.getAttributionTag(), intents, resolvedTypes, + token, options, userId); checkStartActivityResult(result, intents[0]); return result; } catch (RemoteException e) { @@ -1860,10 +1859,10 @@ public class Instrumentation { try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(who); - int result = ActivityTaskManager.getService() - .startActivity(whoThread, who.getBasePackageName(), who.getFeatureId(), intent, - intent.resolveTypeIfNeeded(who.getContentResolver()), - token, target, requestCode, 0, null, options); + int result = ActivityTaskManager.getService().startActivity(whoThread, + who.getBasePackageName(), who.getAttributionTag(), intent, + intent.resolveTypeIfNeeded(who.getContentResolver()), token, target, + requestCode, 0, null, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); @@ -1927,11 +1926,10 @@ public class Instrumentation { try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(who); - int result = ActivityTaskManager.getService() - .startActivityAsUser(whoThread, who.getBasePackageName(), who.getFeatureId(), - intent, intent.resolveTypeIfNeeded(who.getContentResolver()), - token, resultWho, - requestCode, 0, null, options, user.getIdentifier()); + int result = ActivityTaskManager.getService().startActivityAsUser(whoThread, + who.getBasePackageName(), who.getAttributionTag(), intent, + intent.resolveTypeIfNeeded(who.getContentResolver()), token, resultWho, + requestCode, 0, null, options, user.getIdentifier()); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); @@ -2022,7 +2020,7 @@ public class Instrumentation { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(who); int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(), - who.getFeatureId(), intent, + who.getAttributionTag(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), options); checkStartActivityResult(result, intent); } catch (RemoteException e) { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 32e7d84e6083..061e5ff35f55 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -21,6 +21,7 @@ import static android.graphics.drawable.Icon.TYPE_URI; import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP; import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast; +import static com.android.internal.widget.ConversationLayout.CONVERSATION_LAYOUT_ENABLED; import android.annotation.ColorInt; import android.annotation.DimenRes; @@ -389,6 +390,7 @@ public class Notification implements Parcelable STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_text); STANDARD_LAYOUTS.add(R.layout.notification_template_material_inbox); STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging); + STANDARD_LAYOUTS.add(R.layout.notification_template_material_conversation); STANDARD_LAYOUTS.add(R.layout.notification_template_material_media); STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media); STANDARD_LAYOUTS.add(R.layout.notification_template_header); @@ -5138,7 +5140,7 @@ public class Notification implements Parcelable int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p); contentView.setDrawableTint(R.id.expand_button, false, color, PorterDuff.Mode.SRC_ATOP); - contentView.setInt(R.id.notification_header, "setOriginalNotificationColor", + contentView.setInt(R.id.expand_button, "setOriginalNotificationColor", color); } @@ -6116,7 +6118,9 @@ public class Notification implements Parcelable } private int getMessagingLayoutResource() { - return R.layout.notification_template_material_messaging; + return CONVERSATION_LAYOUT_ENABLED + ? R.layout.notification_template_material_conversation + : R.layout.notification_template_material_messaging; } private int getActionLayoutResource() { @@ -6221,6 +6225,17 @@ public class Notification implements Parcelable } return loadHeaderAppName(); } + + /** + * @return if this builder uses a template + * + * @hide + */ + public boolean usesTemplate() { + return (mN.contentView == null && mN.headsUpContentView == null + && mN.bigContentView == null) + || (mStyle != null && mStyle.displayCustomViewInline()); + } } /** @@ -7379,7 +7394,7 @@ public class Notification implements Parcelable public RemoteViews makeContentView(boolean increasedHeight) { mBuilder.mOriginalActions = mBuilder.mActions; mBuilder.mActions = new ArrayList<>(); - RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */, + RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */, false /* hideLargeIcon */); mBuilder.mActions = mBuilder.mOriginalActions; mBuilder.mOriginalActions = null; @@ -7469,19 +7484,18 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeBigContentView() { - return makeMessagingView(false /* displayImagesAtEnd */, true /* hideLargeIcon */); + return makeMessagingView(false /* isCollapsed */, true /* hideLargeIcon */); } /** * Create a messaging layout. * - * @param displayImagesAtEnd should images be displayed at the end of the content instead - * of inline. + * @param isCollapsed Should this use the collapsed layout * @param hideRightIcons Should the reply affordance be shown at the end of the notification * @return the created remoteView. */ @NonNull - private RemoteViews makeMessagingView(boolean displayImagesAtEnd, boolean hideRightIcons) { + private RemoteViews makeMessagingView(boolean isCollapsed, boolean hideRightIcons) { CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle) ? super.mBigContentTitle : mConversationTitle; @@ -7522,14 +7536,21 @@ public class Notification implements Parcelable mBuilder.getPrimaryTextColor(p)); contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor", mBuilder.getSecondaryTextColor(p)); - contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd", - displayImagesAtEnd); + contentView.setInt(R.id.status_bar_latest_event_content, + "setNotificationBackgroundColor", + mBuilder.resolveBackgroundColor(p)); + contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed", + isCollapsed); contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement", avatarReplacement); contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement", nameReplacement); contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsOneToOne", isOneToOne); + contentView.setCharSequence(R.id.status_bar_latest_event_content, + "setConversationTitle", conversationTitle); + contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon", + mBuilder.mN.mLargeIcon); contentView.setBundle(R.id.status_bar_latest_event_content, "setData", mBuilder.mN.extras); return contentView; @@ -7590,9 +7611,11 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { - RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */, + RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */, true /* hideLargeIcon */); - remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1); + if (!CONVERSATION_LAYOUT_ENABLED) { + remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1); + } return remoteViews; } diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 3f2ec4487668..94b237cd1f5b 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -134,6 +134,7 @@ public final class NotificationChannel implements Parcelable { * @hide */ @SystemApi + @TestApi public static final int USER_LOCKED_SOUND = 0x00000020; /** @@ -331,6 +332,7 @@ public final class NotificationChannel implements Parcelable { /** * @hide */ + @TestApi public void lockFields(int field) { mUserLockedFields |= field; } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index f68c929144c8..792f840638a9 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -355,8 +355,8 @@ public final class PendingIntent implements Parcelable { intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(), - null, null, requestCode, new Intent[] { intent }, + ActivityManager.INTENT_SENDER_ACTIVITY, packageName, + context.getAttributionTag(), null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, options, context.getUserId()); return target != null ? new PendingIntent(target) : null; @@ -381,8 +381,8 @@ public final class PendingIntent implements Parcelable { intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(), - null, null, requestCode, new Intent[] { intent }, + ActivityManager.INTENT_SENDER_ACTIVITY, packageName, + context.getAttributionTag(), null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, options, user.getIdentifier()); return target != null ? new PendingIntent(target) : null; @@ -498,9 +498,9 @@ public final class PendingIntent implements Parcelable { try { IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(), - null, null, requestCode, intents, resolvedTypes, flags, options, - context.getUserId()); + ActivityManager.INTENT_SENDER_ACTIVITY, packageName, + context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes, + flags, options, context.getUserId()); return target != null ? new PendingIntent(target) : null; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -524,8 +524,8 @@ public final class PendingIntent implements Parcelable { try { IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(), - null, null, requestCode, intents, resolvedTypes, + ActivityManager.INTENT_SENDER_ACTIVITY, packageName, + context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes, flags, options, user.getIdentifier()); return target != null ? new PendingIntent(target) : null; } catch (RemoteException e) { @@ -576,8 +576,8 @@ public final class PendingIntent implements Parcelable { intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_BROADCAST, packageName, context.getFeatureId(), - null, null, requestCode, new Intent[] { intent }, + ActivityManager.INTENT_SENDER_BROADCAST, packageName, + context.getAttributionTag(), null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, null, userHandle.getIdentifier()); return target != null ? new PendingIntent(target) : null; @@ -655,7 +655,7 @@ public final class PendingIntent implements Parcelable { intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - serviceKind, packageName, context.getFeatureId(), + serviceKind, packageName, context.getAttributionTag(), null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, null, context.getUserId()); diff --git a/core/java/android/app/RuntimeAppOpAccessMessage.java b/core/java/android/app/RuntimeAppOpAccessMessage.java index a81b8e7ab13c..a19f815c4298 100644 --- a/core/java/android/app/RuntimeAppOpAccessMessage.java +++ b/core/java/android/app/RuntimeAppOpAccessMessage.java @@ -44,7 +44,7 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { /** Name of package for which runtime app op access message was collected */ private final @NonNull String mPackageName; /** Feature of package for which runtime app op access message was collected */ - private final @Nullable String mFeatureId; + private final @Nullable String mAttributionTag; /** Message collected (including stacktrace for synchronous ops) */ private final @NonNull String mMessage; /** Sampling strategy used to collect this message. */ @@ -63,8 +63,8 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { * Op code of operation access which was collected * @param packageName * Name of package for which runtime app op access message was collected - * @param featureId - * Feature of package for which runtime app op access message was collected + * @param attributionTag + * Attribution tag for which runtime app op access message was collected * @param message * Message collected (including stacktrace for synchronous ops) * @param samplingStrategy @@ -75,7 +75,7 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { @IntRange(from = 0L) int uid, @IntRange(from = 0L) int opCode, @NonNull String packageName, - @Nullable String featureId, + @Nullable String attributionTag, @NonNull String message, @AppOpsManager.SamplingStrategy int samplingStrategy) { this.mUid = uid; @@ -90,7 +90,7 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { this.mPackageName = packageName; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mPackageName); - this.mFeatureId = featureId; + this.mAttributionTag = attributionTag; this.mMessage = message; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mMessage); @@ -134,11 +134,11 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { } /** - * Feature of package for which runtime app op access message was collected + * Attribution tag for which runtime app op access message was collected */ @DataClass.Generated.Member - public @Nullable String getFeatureId() { - return mFeatureId; + public @Nullable String getAttributionTag() { + return mAttributionTag; } /** @@ -164,12 +164,12 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mFeatureId != null) flg |= 0x8; + if (mAttributionTag != null) flg |= 0x8; dest.writeByte(flg); dest.writeInt(mUid); dest.writeInt(mOpCode); dest.writeString(mPackageName); - if (mFeatureId != null) dest.writeString(mFeatureId); + if (mAttributionTag != null) dest.writeString(mAttributionTag); dest.writeString(mMessage); dest.writeInt(mSamplingStrategy); } @@ -189,7 +189,7 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { int uid = in.readInt(); int opCode = in.readInt(); String packageName = in.readString(); - String featureId = (flg & 0x8) == 0 ? null : in.readString(); + String attributionTag = (flg & 0x8) == 0 ? null : in.readString(); String message = in.readString(); int samplingStrategy = in.readInt(); @@ -205,7 +205,7 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { this.mPackageName = packageName; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mPackageName); - this.mFeatureId = featureId; + this.mAttributionTag = attributionTag; this.mMessage = message; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mMessage); @@ -234,7 +234,7 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { time = 1581517099127L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/app/RuntimeAppOpAccessMessage.java", - inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.app.AppOpsManager.SamplingStrategy int mSamplingStrategy\npublic @android.annotation.NonNull java.lang.String getOp()\nclass RuntimeAppOpAccessMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")*/ + inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.app.AppOpsManager.SamplingStrategy int mSamplingStrategy\npublic @android.annotation.NonNull java.lang.String getOp()\nclass RuntimeAppOpAccessMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")*/ @Deprecated private void __metadata() {} diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java index 13b90ca6ced1..0a880dc0cbb7 100644 --- a/core/java/android/app/SyncNotedAppOp.java +++ b/core/java/android/app/SyncNotedAppOp.java @@ -43,24 +43,24 @@ public final class SyncNotedAppOp implements Parcelable { /** op code of synchronous appop noted */ private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode; - /** featureId of synchronous appop noted */ - private final @Nullable String mFeatureId; + /** attributionTag of synchronous appop noted */ + private final @Nullable String mAttributionTag; /** * Creates a new SyncNotedAppOp. * * @param opCode * op code of synchronous appop noted - * @param featureId - * featureId of synchronous appop noted + * @param attributionTag + * attributionTag of synchronous appop noted */ - public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String featureId) { + public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String attributionTag) { this.mOpCode = opCode; com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mOpCode, "from", 0, "to", AppOpsManager._NUM_OP - 1); - this.mFeatureId = featureId; + this.mAttributionTag = attributionTag; } /** @@ -84,11 +84,11 @@ public final class SyncNotedAppOp implements Parcelable { /** - * featureId of synchronous appop noted + * attributionTag of synchronous appop noted */ @DataClass.Generated.Member - public @Nullable String getFeatureId() { - return mFeatureId; + public @Nullable String getAttributionTag() { + return mAttributionTag; } @Override @@ -105,7 +105,7 @@ public final class SyncNotedAppOp implements Parcelable { //noinspection PointlessBooleanExpression return true && mOpCode == that.mOpCode - && java.util.Objects.equals(mFeatureId, that.mFeatureId); + && java.util.Objects.equals(mAttributionTag, that.mAttributionTag); } @Override @@ -116,7 +116,7 @@ public final class SyncNotedAppOp implements Parcelable { int _hash = 1; _hash = 31 * _hash + mOpCode; - _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId); + _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag); return _hash; } @@ -127,10 +127,10 @@ public final class SyncNotedAppOp implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mFeatureId != null) flg |= 0x2; + if (mAttributionTag != null) flg |= 0x2; dest.writeByte(flg); dest.writeInt(mOpCode); - if (mFeatureId != null) dest.writeString(mFeatureId); + if (mAttributionTag != null) dest.writeString(mAttributionTag); } @Override @@ -146,14 +146,14 @@ public final class SyncNotedAppOp implements Parcelable { byte flg = in.readByte(); int opCode = in.readInt(); - String featureId = (flg & 0x2) == 0 ? null : in.readString(); + String attributionTag = (flg & 0x2) == 0 ? null : in.readString(); this.mOpCode = opCode; com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mOpCode, "from", 0, "to", AppOpsManager._NUM_OP - 1); - this.mFeatureId = featureId; + this.mAttributionTag = attributionTag; // onConstructed(); // You can define this method to get a callback } @@ -176,7 +176,7 @@ public final class SyncNotedAppOp implements Parcelable { time = 1579188889960L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java", - inputSignatures = "private final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\npublic @android.annotation.NonNull java.lang.String getOp()\npublic @android.annotation.SystemApi int getOpCode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genConstructor=false)")*/ + inputSignatures = "private final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\npublic @android.annotation.NonNull java.lang.String getOp()\npublic @android.annotation.SystemApi int getOpCode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genConstructor=false)")*/ @Deprecated private void __metadata() {} diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index b892b8e51c88..93772de0ba30 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -16,6 +16,7 @@ package android.app; +import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.TaskSnapshot; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -53,7 +54,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage - public void onPinnedActivityRestartAttempt(boolean clearedTask) throws RemoteException { + public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) throws RemoteException { } @Override @@ -68,14 +70,14 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onActivityLaunchOnSecondaryDisplayFailed(ActivityManager.RunningTaskInfo taskInfo, + public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo, int requestedDisplayId) throws RemoteException { onActivityLaunchOnSecondaryDisplayFailed(); } /** * @deprecated see {@link - * #onActivityLaunchOnSecondaryDisplayFailed(ActivityManager.RunningTaskInfo, int)} + * #onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo, int)} */ @Deprecated @UnsupportedAppUsage @@ -84,7 +86,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage - public void onActivityLaunchOnSecondaryDisplayRerouted(ActivityManager.RunningTaskInfo taskInfo, + public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo, int requestedDisplayId) throws RemoteException { } @@ -98,13 +100,13 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) + public void onTaskMovedToFront(RunningTaskInfo taskInfo) throws RemoteException { onTaskMovedToFront(taskInfo.taskId); } /** - * @deprecated see {@link #onTaskMovedToFront(ActivityManager.RunningTaskInfo)} + * @deprecated see {@link #onTaskMovedToFront(RunningTaskInfo)} */ @Deprecated @UnsupportedAppUsage @@ -112,26 +114,26 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onTaskRemovalStarted(ActivityManager.RunningTaskInfo taskInfo) + public void onTaskRemovalStarted(RunningTaskInfo taskInfo) throws RemoteException { onTaskRemovalStarted(taskInfo.taskId); } /** - * @deprecated see {@link #onTaskRemovalStarted(ActivityManager.RunningTaskInfo)} + * @deprecated see {@link #onTaskRemovalStarted(RunningTaskInfo)} */ @Deprecated public void onTaskRemovalStarted(int taskId) throws RemoteException { } @Override - public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo) + public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) throws RemoteException { onTaskDescriptionChanged(taskInfo.taskId, taskInfo.taskDescription); } /** - * @deprecated see {@link #onTaskDescriptionChanged(ActivityManager.RunningTaskInfo)} + * @deprecated see {@link #onTaskDescriptionChanged(RunningTaskInfo)} */ @Deprecated public void onTaskDescriptionChanged(int taskId, ActivityManager.TaskDescription td) @@ -166,7 +168,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) + public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) throws RemoteException { } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index d9405e18a162..1b1568ac5a12 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -478,7 +478,7 @@ public class WallpaperManager { try { Bundle params = new Bundle(); ParcelFileDescriptor pfd = mService.getWallpaperWithFeature( - context.getOpPackageName(), context.getFeatureId(), this, FLAG_SYSTEM, + context.getOpPackageName(), context.getAttributionTag(), this, FLAG_SYSTEM, params, userId); if (pfd != null) { @@ -1069,7 +1069,7 @@ public class WallpaperManager { try { Bundle outParams = new Bundle(); return sGlobals.mService.getWallpaperWithFeature(mContext.getOpPackageName(), - mContext.getFeatureId(), null, which, outParams, userId); + mContext.getAttributionTag(), null, which, outParams, userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SecurityException e) { @@ -1858,13 +1858,12 @@ public class WallpaperManager { * * @hide */ - public void setWallpaperZoomOut(float zoom) { + public void setWallpaperZoomOut(IBinder windowToken, float zoom) { if (zoom < 0 || zoom > 1f) { throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom); } try { - sGlobals.mService.setWallpaperZoomOut(zoom, mContext.getOpPackageName(), - mContext.getDisplayId()); + WindowManagerGlobal.getWindowSession().setWallpaperZoomOut(windowToken, zoom); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index c532279676a0..347648a78886 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -6001,7 +6001,7 @@ public class DevicePolicyManager { * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param required Whether auto time is set required or not. * @throws SecurityException if {@code admin} is not a device owner. - * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #setAutoTime} + * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #setAutoTimeEnabled} * to turn auto time on or off and use {@link UserManager#DISALLOW_CONFIG_DATE_TIME} * to prevent the user from changing this setting. */ @@ -6019,7 +6019,7 @@ public class DevicePolicyManager { /** * @return true if auto time is required. - * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #getAutoTime} + * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #getAutoTimeEnabled} */ @Deprecated public boolean getAutoTimeRequired() { @@ -6049,10 +6049,10 @@ public class DevicePolicyManager { * @throws SecurityException if caller is not a device owner, a profile owner for the * primary user, or a profile owner of an organization-owned managed profile. */ - public void setAutoTime(@NonNull ComponentName admin, boolean enabled) { + public void setAutoTimeEnabled(@NonNull ComponentName admin, boolean enabled) { if (mService != null) { try { - mService.setAutoTime(admin, enabled); + mService.setAutoTimeEnabled(admin, enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -6064,10 +6064,10 @@ public class DevicePolicyManager { * @throws SecurityException if caller is not a device owner, a profile owner for the * primary user, or a profile owner of an organization-owned managed profile. */ - public boolean getAutoTime(@NonNull ComponentName admin) { + public boolean getAutoTimeEnabled(@NonNull ComponentName admin) { if (mService != null) { try { - return mService.getAutoTime(admin); + return mService.getAutoTimeEnabled(admin); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -6090,11 +6090,11 @@ public class DevicePolicyManager { * @throws SecurityException if caller is not a device owner, a profile owner for the * primary user, or a profile owner of an organization-owned managed profile. */ - public void setAutoTimeZone(@NonNull ComponentName admin, boolean enabled) { + public void setAutoTimeZoneEnabled(@NonNull ComponentName admin, boolean enabled) { throwIfParentInstance("setAutoTimeZone"); if (mService != null) { try { - mService.setAutoTimeZone(admin, enabled); + mService.setAutoTimeZoneEnabled(admin, enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -6106,11 +6106,11 @@ public class DevicePolicyManager { * @throws SecurityException if caller is not a device owner, a profile owner for the * primary user, or a profile owner of an organization-owned managed profile. */ - public boolean getAutoTimeZone(@NonNull ComponentName admin) { + public boolean getAutoTimeZoneEnabled(@NonNull ComponentName admin) { throwIfParentInstance("getAutoTimeZone"); if (mService != null) { try { - return mService.getAutoTimeZone(admin); + return mService.getAutoTimeZoneEnabled(admin); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -8804,10 +8804,11 @@ public class DevicePolicyManager { * <p> * The following settings used to be supported, but can be controlled in other ways: * <ul> - * <li>{@link android.provider.Settings.Global#AUTO_TIME} : Use {@link #setAutoTime} and + * <li>{@link android.provider.Settings.Global#AUTO_TIME} : Use {@link #setAutoTimeEnabled} and * {@link UserManager#DISALLOW_CONFIG_DATE_TIME} instead.</li> - * <li>{@link android.provider.Settings.Global#AUTO_TIME_ZONE} : Use {@link #setAutoTimeZone} - * and {@link UserManager#DISALLOW_CONFIG_DATE_TIME} instead.</li> + * <li>{@link android.provider.Settings.Global#AUTO_TIME_ZONE} : Use + * {@link #setAutoTimeZoneEnabled} and {@link UserManager#DISALLOW_CONFIG_DATE_TIME} + * instead.</li> * <li>{@link android.provider.Settings.Global#DATA_ROAMING} : Use * {@link UserManager#DISALLOW_DATA_ROAMING} instead.</li> * </ul> @@ -11984,17 +11985,17 @@ public class DevicePolicyManager { * must handle this intent. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with - * @param timeoutMs Maximum time the profile is allowed to be off in milliseconds or 0 if + * @param timeoutMillis Maximum time the profile is allowed to be off in milliseconds or 0 if * not limited. * @throws IllegalStateException if the profile owner doesn't have an activity that handles * {@link #ACTION_CHECK_POLICY_COMPLIANCE} * @see #setPersonalAppsSuspended */ - public void setManagedProfileMaximumTimeOff(@NonNull ComponentName admin, long timeoutMs) { + public void setManagedProfileMaximumTimeOff(@NonNull ComponentName admin, long timeoutMillis) { throwIfParentInstance("setManagedProfileMaximumTimeOff"); if (mService != null) { try { - mService.setManagedProfileMaximumTimeOff(admin, timeoutMs); + mService.setManagedProfileMaximumTimeOff(admin, timeoutMillis); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index da48663145e1..f07123979529 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -308,11 +308,11 @@ interface IDevicePolicyManager { void setAutoTimeRequired(in ComponentName who, boolean required); boolean getAutoTimeRequired(); - void setAutoTime(in ComponentName who, boolean enabled); - boolean getAutoTime(in ComponentName who); + void setAutoTimeEnabled(in ComponentName who, boolean enabled); + boolean getAutoTimeEnabled(in ComponentName who); - void setAutoTimeZone(in ComponentName who, boolean enabled); - boolean getAutoTimeZone(in ComponentName who); + void setAutoTimeZoneEnabled(in ComponentName who, boolean enabled); + boolean getAutoTimeZoneEnabled(in ComponentName who); void setForceEphemeralUsers(in ComponentName who, boolean forceEpehemeralUsers); boolean getForceEphemeralUsers(in ComponentName who); diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index db4f1de1f743..917eeb84c7a5 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -636,7 +636,6 @@ public final class RoleManager { * @hide */ @Nullable - @SystemApi public String getDefaultSmsPackage(@UserIdInt int userId) { try { return mService.getDefaultSmsPackage(userId); diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index ccd8199b8373..0d461f5fa32c 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -174,7 +174,7 @@ public class AppWidgetManager { public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE"; /** - * An intent extra that contains one appWidgetId. + * An intent extra (int) that contains one appWidgetId. * <p> * The value will be an int that can be retrieved like this: * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/AppWidgetHostActivity.java getExtra_EXTRA_APPWIDGET_ID} @@ -182,7 +182,7 @@ public class AppWidgetManager { public static final String EXTRA_APPWIDGET_ID = "appWidgetId"; /** - * A bundle extra that contains whether or not an app has finished restoring a widget. + * A bundle extra (boolean) that contains whether or not an app has finished restoring a widget. * <p> After restore, the app should set OPTION_APPWIDGET_RESTORE_COMPLETED to true on its * widgets followed by calling {@link #updateAppWidget} to update the views. * @@ -192,22 +192,26 @@ public class AppWidgetManager { /** - * A bundle extra that contains the lower bound on the current width, in dips, of a widget instance. + * A bundle extra (int) that contains the lower bound on the current width, in dips, of a + * widget instance. */ public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth"; /** - * A bundle extra that contains the lower bound on the current height, in dips, of a widget instance. + * A bundle extra (int) that contains the lower bound on the current height, in dips, of a + * widget instance. */ public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight"; /** - * A bundle extra that contains the upper bound on the current width, in dips, of a widget instance. + * A bundle extra (int) that contains the upper bound on the current width, in dips, of a + * widget instance. */ public static final String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth"; /** - * A bundle extra that contains the upper bound on the current width, in dips, of a widget instance. + * A bundle extra (int) that contains the upper bound on the current width, in dips, of a + * widget instance. */ public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight"; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 6ae68fcad6f4..608b563bfc76 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -849,7 +849,7 @@ public final class BluetoothAdapter { synchronized (mLock) { if (sBluetoothLeScanner == null) { sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } return sBluetoothLeScanner; @@ -1663,11 +1663,11 @@ public final class BluetoothAdapter { return ActivityThread.currentOpPackageName(); } - private String getFeatureId() { + private String getAttributionTag() { // Workaround for legacy API for getting a BluetoothAdapter not // passing a context if (mContext != null) { - return mContext.getFeatureId(); + return mContext.getAttributionTag(); } return null; } @@ -1709,7 +1709,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(getOpPackageName(), getFeatureId()); + return mService.startDiscovery(getOpPackageName(), getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index 7ff64663349b..3b4fe0a30b80 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -62,7 +62,7 @@ public final class BluetoothManager { * @hide */ public BluetoothManager(Context context) { - if (context.getFeatureId() == null) { + if (context.getAttributionTag() == null) { context = context.getApplicationContext(); if (context == null) { throw new IllegalArgumentException( diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index bd3298c79fff..d8e8b27d0621 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -230,12 +230,12 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public Cursor query(String callingPkg, @Nullable String featureId, Uri uri, + public Cursor query(String callingPkg, @Nullable String attributionTag, Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - if (enforceReadPermission(callingPkg, featureId, uri, null) + if (enforceReadPermission(callingPkg, attributionTag, uri, null) != AppOpsManager.MODE_ALLOWED) { // The caller has no access to the data, so return an empty cursor with // the columns in the requested order. The caller may ask for an invalid @@ -253,7 +253,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall // columns. We then use the column names to return an empty cursor. Cursor cursor; final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { cursor = mInterface.query( uri, projection, queryArgs, @@ -272,7 +272,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } Trace.traceBegin(TRACE_TAG_DATABASE, "query"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return mInterface.query( uri, projection, queryArgs, @@ -308,15 +308,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public Uri insert(String callingPkg, @Nullable String featureId, Uri uri, + public Uri insert(String callingPkg, @Nullable String attributionTag, Uri uri, ContentValues initialValues, Bundle extras) { uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); uri = maybeGetUriWithoutUserId(uri); - if (enforceWritePermission(callingPkg, featureId, uri, null) + if (enforceWritePermission(callingPkg, attributionTag, uri, null) != AppOpsManager.MODE_ALLOWED) { final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return rejectInsert(uri, initialValues); } finally { @@ -325,7 +325,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } Trace.traceBegin(TRACE_TAG_DATABASE, "insert"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return maybeAddUserId(mInterface.insert(uri, initialValues, extras), userId); } catch (RemoteException e) { @@ -337,17 +337,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public int bulkInsert(String callingPkg, @Nullable String featureId, Uri uri, + public int bulkInsert(String callingPkg, @Nullable String attributionTag, Uri uri, ContentValues[] initialValues) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - if (enforceWritePermission(callingPkg, featureId, uri, null) + if (enforceWritePermission(callingPkg, attributionTag, uri, null) != AppOpsManager.MODE_ALLOWED) { return 0; } Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return mInterface.bulkInsert(uri, initialValues); } catch (RemoteException e) { @@ -359,8 +359,9 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId, - String authority, ArrayList<ContentProviderOperation> operations) + public ContentProviderResult[] applyBatch(String callingPkg, + @Nullable String attributionTag, String authority, + ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { validateIncomingAuthority(authority); int numOperations = operations.size(); @@ -377,13 +378,13 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall operations.set(i, operation); } if (operation.isReadOperation()) { - if (enforceReadPermission(callingPkg, featureId, uri, null) + if (enforceReadPermission(callingPkg, attributionTag, uri, null) != AppOpsManager.MODE_ALLOWED) { throw new OperationApplicationException("App op not allowed", 0); } } if (operation.isWriteOperation()) { - if (enforceWritePermission(callingPkg, featureId, uri, null) + if (enforceWritePermission(callingPkg, attributionTag, uri, null) != AppOpsManager.MODE_ALLOWED) { throw new OperationApplicationException("App op not allowed", 0); } @@ -391,7 +392,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { ContentProviderResult[] results = mInterface.applyBatch(authority, operations); @@ -413,16 +414,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public int delete(String callingPkg, @Nullable String featureId, Uri uri, Bundle extras) { + public int delete(String callingPkg, @Nullable String attributionTag, Uri uri, + Bundle extras) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - if (enforceWritePermission(callingPkg, featureId, uri, null) + if (enforceWritePermission(callingPkg, attributionTag, uri, null) != AppOpsManager.MODE_ALLOWED) { return 0; } Trace.traceBegin(TRACE_TAG_DATABASE, "delete"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return mInterface.delete(uri, extras); } catch (RemoteException e) { @@ -434,17 +436,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public int update(String callingPkg, @Nullable String featureId, Uri uri, + public int update(String callingPkg, @Nullable String attributionTag, Uri uri, ContentValues values, Bundle extras) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - if (enforceWritePermission(callingPkg, featureId, uri, null) + if (enforceWritePermission(callingPkg, attributionTag, uri, null) != AppOpsManager.MODE_ALLOWED) { return 0; } Trace.traceBegin(TRACE_TAG_DATABASE, "update"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return mInterface.update(uri, values, extras); } catch (RemoteException e) { @@ -456,15 +458,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, + public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag, Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken) throws FileNotFoundException { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - enforceFilePermission(callingPkg, featureId, uri, mode, callerToken); + enforceFilePermission(callingPkg, attributionTag, uri, mode, callerToken); Trace.traceBegin(TRACE_TAG_DATABASE, "openFile"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return mInterface.openFile( uri, mode, CancellationSignal.fromTransport(cancellationSignal)); @@ -477,15 +479,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId, + public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag, Uri uri, String mode, ICancellationSignal cancellationSignal) throws FileNotFoundException { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - enforceFilePermission(callingPkg, featureId, uri, mode, null); + enforceFilePermission(callingPkg, attributionTag, uri, mode, null); Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return mInterface.openAssetFile( uri, mode, CancellationSignal.fromTransport(cancellationSignal)); @@ -498,13 +500,13 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public Bundle call(String callingPkg, @Nullable String featureId, String authority, + public Bundle call(String callingPkg, @Nullable String attributionTag, String authority, String method, @Nullable String arg, @Nullable Bundle extras) { validateIncomingAuthority(authority); Bundle.setDefusable(extras, true); Trace.traceBegin(TRACE_TAG_DATABASE, "call"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return mInterface.call(authority, method, arg, extras); } catch (RemoteException e) { @@ -532,15 +534,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall @Override public AssetFileDescriptor openTypedAssetFile(String callingPkg, - @Nullable String featureId, Uri uri, String mimeType, Bundle opts, + @Nullable String attributionTag, Uri uri, String mimeType, Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException { Bundle.setDefusable(opts, true); uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - enforceFilePermission(callingPkg, featureId, uri, "r", null); + enforceFilePermission(callingPkg, attributionTag, uri, "r", null); Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return mInterface.openTypedAssetFile( uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal)); @@ -558,17 +560,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) { + public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri) { uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); uri = getUriWithoutUserId(uri); - if (enforceReadPermission(callingPkg, featureId, uri, null) + if (enforceReadPermission(callingPkg, attributionTag, uri, null) != AppOpsManager.MODE_ALLOWED) { return null; } Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return maybeAddUserId(mInterface.canonicalize(uri), userId); } catch (RemoteException e) { @@ -580,26 +582,26 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri, + public void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri, RemoteCallback callback) { final Bundle result = new Bundle(); result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, - canonicalize(callingPkg, featureId, uri)); + canonicalize(callingPkg, attributionTag, uri)); callback.sendResult(result); } @Override - public Uri uncanonicalize(String callingPkg, String featureId, Uri uri) { + public Uri uncanonicalize(String callingPkg, String attributionTag, Uri uri) { uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); uri = getUriWithoutUserId(uri); - if (enforceReadPermission(callingPkg, featureId, uri, null) + if (enforceReadPermission(callingPkg, attributionTag, uri, null) != AppOpsManager.MODE_ALLOWED) { return null; } Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return maybeAddUserId(mInterface.uncanonicalize(uri), userId); } catch (RemoteException e) { @@ -611,17 +613,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public boolean refresh(String callingPkg, String featureId, Uri uri, Bundle extras, + public boolean refresh(String callingPkg, String attributionTag, Uri uri, Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException { uri = validateIncomingUri(uri); uri = getUriWithoutUserId(uri); - if (enforceReadPermission(callingPkg, featureId, uri, null) + if (enforceReadPermission(callingPkg, attributionTag, uri, null) != AppOpsManager.MODE_ALLOWED) { return false; } Trace.traceBegin(TRACE_TAG_DATABASE, "refresh"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return mInterface.refresh(uri, extras, CancellationSignal.fromTransport(cancellationSignal)); @@ -632,13 +634,13 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, + public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri, int uid, int modeFlags) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission"); final Pair<String, String> original = setCallingPackage( - new Pair<>(callingPkg, featureId)); + new Pair<>(callingPkg, attributionTag)); try { return mInterface.checkUriPermission(uri, uid, modeFlags); } catch (RemoteException e) { @@ -649,47 +651,50 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } } - private void enforceFilePermission(String callingPkg, @Nullable String featureId, Uri uri, - String mode, IBinder callerToken) throws FileNotFoundException, SecurityException { + private void enforceFilePermission(String callingPkg, @Nullable String attributionTag, + Uri uri, String mode, IBinder callerToken) + throws FileNotFoundException, SecurityException { if (mode != null && mode.indexOf('w') != -1) { - if (enforceWritePermission(callingPkg, featureId, uri, callerToken) + if (enforceWritePermission(callingPkg, attributionTag, uri, callerToken) != AppOpsManager.MODE_ALLOWED) { throw new FileNotFoundException("App op not allowed"); } } else { - if (enforceReadPermission(callingPkg, featureId, uri, callerToken) + if (enforceReadPermission(callingPkg, attributionTag, uri, callerToken) != AppOpsManager.MODE_ALLOWED) { throw new FileNotFoundException("App op not allowed"); } } } - private int enforceReadPermission(String callingPkg, @Nullable String featureId, Uri uri, - IBinder callerToken) + private int enforceReadPermission(String callingPkg, @Nullable String attributionTag, + Uri uri, IBinder callerToken) throws SecurityException { - final int mode = enforceReadPermissionInner(uri, callingPkg, featureId, callerToken); + final int mode = enforceReadPermissionInner(uri, callingPkg, attributionTag, + callerToken); if (mode != MODE_ALLOWED) { return mode; } - return noteProxyOp(callingPkg, featureId, mReadOp); + return noteProxyOp(callingPkg, attributionTag, mReadOp); } - private int enforceWritePermission(String callingPkg, String featureId, Uri uri, + private int enforceWritePermission(String callingPkg, String attributionTag, Uri uri, IBinder callerToken) throws SecurityException { - final int mode = enforceWritePermissionInner(uri, callingPkg, featureId, callerToken); + final int mode = enforceWritePermissionInner(uri, callingPkg, attributionTag, + callerToken); if (mode != MODE_ALLOWED) { return mode; } - return noteProxyOp(callingPkg, featureId, mWriteOp); + return noteProxyOp(callingPkg, attributionTag, mWriteOp); } - private int noteProxyOp(String callingPkg, String featureId, int op) { + private int noteProxyOp(String callingPkg, String attributionTag, int op) { if (op != AppOpsManager.OP_NONE) { int mode = mAppOpsManager.noteProxyOp(op, callingPkg, Binder.getCallingUid(), - featureId, null); + attributionTag, null); return mode == MODE_DEFAULT ? MODE_IGNORED : mode; } @@ -711,19 +716,19 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * associated with that permission. */ private int checkPermissionAndAppOp(String permission, String callingPkg, - @Nullable String featureId, IBinder callerToken) { + @Nullable String attributionTag, IBinder callerToken) { if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(), callerToken) != PERMISSION_GRANTED) { return MODE_ERRORED; } - return mTransport.noteProxyOp(callingPkg, featureId, + return mTransport.noteProxyOp(callingPkg, attributionTag, AppOpsManager.permissionToOpCode(permission)); } /** {@hide} */ protected int enforceReadPermissionInner(Uri uri, String callingPkg, - @Nullable String featureId, IBinder callerToken) throws SecurityException { + @Nullable String attributionTag, IBinder callerToken) throws SecurityException { final Context context = getContext(); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -737,7 +742,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall if (mExported && checkUser(pid, uid, context)) { final String componentPerm = getReadPermission(); if (componentPerm != null) { - final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId, + final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, attributionTag, callerToken); if (mode == MODE_ALLOWED) { return MODE_ALLOWED; @@ -757,8 +762,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall for (PathPermission pp : pps) { final String pathPerm = pp.getReadPermission(); if (pathPerm != null && pp.match(path)) { - final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId, - callerToken); + final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, + attributionTag, callerToken); if (mode == MODE_ALLOWED) { return MODE_ALLOWED; } else { @@ -807,7 +812,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall /** {@hide} */ protected int enforceWritePermissionInner(Uri uri, String callingPkg, - @Nullable String featureId, IBinder callerToken) throws SecurityException { + @Nullable String attributionTag, IBinder callerToken) throws SecurityException { final Context context = getContext(); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -821,8 +826,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall if (mExported && checkUser(pid, uid, context)) { final String componentPerm = getWritePermission(); if (componentPerm != null) { - final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId, - callerToken); + final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, + attributionTag, callerToken); if (mode == MODE_ALLOWED) { return MODE_ALLOWED; } else { @@ -841,8 +846,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall for (PathPermission pp : pps) { final String pathPerm = pp.getWritePermission(); if (pathPerm != null && pp.match(path)) { - final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId, - callerToken); + final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, + attributionTag, callerToken); if (mode == MODE_ALLOWED) { return MODE_ALLOWED; } else { @@ -943,16 +948,16 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** - * Return the feature in the package of the caller that initiated the request being + * Return the attribution tag of the caller that initiated the request being * processed on the current thread. Returns {@code null} if not currently processing - * a request of the request is for the default feature. + * a request of the request is for the default attribution. * <p> * This will always return {@code null} when processing * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests. * * @see #getCallingPackage */ - public final @Nullable String getCallingFeatureId() { + public final @Nullable String getCallingAttributionTag() { final Pair<String, String> pkg = mCallingPackage.get(); if (pkg != null) { return pkg.second; @@ -962,6 +967,14 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** + * @removed + */ + @Deprecated + public final @Nullable String getCallingFeatureId() { + return getCallingAttributionTag(); + } + + /** * Return the package name of the caller that initiated the request being * processed on the current thread. The returned package will have * <em>not</em> been verified to belong to the calling UID. Returns diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index a9b786217eef..d0f5ec467458 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -80,7 +80,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { private final IContentProvider mContentProvider; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final String mPackageName; - private final @Nullable String mFeatureId; + private final @Nullable String mAttributionTag; private final String mAuthority; private final boolean mStable; @@ -104,7 +104,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { mContentResolver = contentResolver; mContentProvider = contentProvider; mPackageName = contentResolver.mPackageName; - mFeatureId = contentResolver.mFeatureId; + mAttributionTag = contentResolver.mAttributionTag; mAuthority = authority; mStable = stable; @@ -195,7 +195,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { cancellationSignal.setRemote(remoteCancellationSignal); } final Cursor cursor = mContentProvider.query( - mPackageName, mFeatureId, uri, projection, queryArgs, remoteCancellationSignal); + mPackageName, mAttributionTag, uri, projection, queryArgs, + remoteCancellationSignal); if (cursor == null) { return null; } @@ -255,7 +256,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.canonicalize(mPackageName, mFeatureId, url); + return mContentProvider.canonicalize(mPackageName, mAttributionTag, url); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -273,7 +274,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.uncanonicalize(mPackageName, mFeatureId, url); + return mContentProvider.uncanonicalize(mPackageName, mAttributionTag, url); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -298,7 +299,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { remoteCancellationSignal = mContentProvider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } - return mContentProvider.refresh(mPackageName, mFeatureId, url, extras, + return mContentProvider.refresh(mPackageName, mAttributionTag, url, extras, remoteCancellationSignal); } catch (DeadObjectException e) { if (!mStable) { @@ -318,7 +319,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.checkUriPermission(mPackageName, mFeatureId, uri, uid, + return mContentProvider.checkUriPermission(mPackageName, mAttributionTag, uri, uid, modeFlags); } catch (DeadObjectException e) { if (!mStable) { @@ -344,7 +345,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.insert(mPackageName, mFeatureId, url, initialValues, extras); + return mContentProvider.insert(mPackageName, mAttributionTag, url, initialValues, + extras); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -364,7 +366,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.bulkInsert(mPackageName, mFeatureId, url, initialValues); + return mContentProvider.bulkInsert(mPackageName, mAttributionTag, url, initialValues); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -388,7 +390,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.delete(mPackageName, mFeatureId, url, extras); + return mContentProvider.delete(mPackageName, mAttributionTag, url, extras); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -413,7 +415,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.update(mPackageName, mFeatureId, url, values, extras); + return mContentProvider.update(mPackageName, mAttributionTag, url, values, extras); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -457,8 +459,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { remoteSignal = mContentProvider.createCancellationSignal(); signal.setRemote(remoteSignal); } - return mContentProvider.openFile(mPackageName, mFeatureId, url, mode, remoteSignal, - null); + return mContentProvider.openFile(mPackageName, mAttributionTag, url, mode, + remoteSignal, null); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -502,7 +504,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { remoteSignal = mContentProvider.createCancellationSignal(); signal.setRemote(remoteSignal); } - return mContentProvider.openAssetFile(mPackageName, mFeatureId, url, mode, + return mContentProvider.openAssetFile(mPackageName, mAttributionTag, url, mode, remoteSignal); } catch (DeadObjectException e) { if (!mStable) { @@ -544,7 +546,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { signal.setRemote(remoteSignal); } return mContentProvider.openTypedAssetFile( - mPackageName, mFeatureId, uri, mimeTypeFilter, opts, remoteSignal); + mPackageName, mAttributionTag, uri, mimeTypeFilter, opts, remoteSignal); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -571,7 +573,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.applyBatch(mPackageName, mFeatureId, authority, operations); + return mContentProvider.applyBatch(mPackageName, mAttributionTag, authority, + operations); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -597,7 +600,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.call(mPackageName, mFeatureId, authority, method, arg, extras); + return mContentProvider.call(mPackageName, mAttributionTag, authority, method, arg, + extras); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 31e1fc824ed2..b134c3796b40 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -767,7 +767,7 @@ public abstract class ContentResolver implements ContentInterface { public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) { mContext = context != null ? context : ActivityThread.currentApplication(); mPackageName = mContext.getOpPackageName(); - mFeatureId = mContext.getFeatureId(); + mAttributionTag = mContext.getAttributionTag(); mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; mWrapped = wrapped; } @@ -1144,7 +1144,7 @@ public abstract class ContentResolver implements ContentInterface { cancellationSignal.setRemote(remoteCancellationSignal); } try { - qCursor = unstableProvider.query(mPackageName, mFeatureId, uri, projection, + qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection, queryArgs, remoteCancellationSignal); } catch (DeadObjectException e) { // The remote process has died... but we only hold an unstable @@ -1155,7 +1155,7 @@ public abstract class ContentResolver implements ContentInterface { if (stableProvider == null) { return null; } - qCursor = stableProvider.query(mPackageName, mFeatureId, uri, projection, + qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection, queryArgs, remoteCancellationSignal); } if (qCursor == null) { @@ -1247,7 +1247,7 @@ public abstract class ContentResolver implements ContentInterface { try { final UriResultListener resultListener = new UriResultListener(); - provider.canonicalizeAsync(mPackageName, mFeatureId, url, + provider.canonicalizeAsync(mPackageName, mAttributionTag, url, new RemoteCallback(resultListener)); resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS); return resultListener.result; @@ -1294,7 +1294,7 @@ public abstract class ContentResolver implements ContentInterface { } try { - return provider.uncanonicalize(mPackageName, mFeatureId, url); + return provider.uncanonicalize(mPackageName, mAttributionTag, url); } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. @@ -1346,7 +1346,7 @@ public abstract class ContentResolver implements ContentInterface { remoteCancellationSignal = provider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } - return provider.refresh(mPackageName, mFeatureId, url, extras, + return provider.refresh(mPackageName, mAttributionTag, url, extras, remoteCancellationSignal); } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity @@ -1748,7 +1748,8 @@ public abstract class ContentResolver implements ContentInterface { try { fd = unstableProvider.openAssetFile( - mPackageName, mFeatureId, uri, mode, remoteCancellationSignal); + mPackageName, mAttributionTag, uri, mode, + remoteCancellationSignal); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -1763,7 +1764,7 @@ public abstract class ContentResolver implements ContentInterface { throw new FileNotFoundException("No content provider: " + uri); } fd = stableProvider.openAssetFile( - mPackageName, mFeatureId, uri, mode, remoteCancellationSignal); + mPackageName, mAttributionTag, uri, mode, remoteCancellationSignal); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -1914,7 +1915,8 @@ public abstract class ContentResolver implements ContentInterface { try { fd = unstableProvider.openTypedAssetFile( - mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal); + mPackageName, mAttributionTag, uri, mimeType, opts, + remoteCancellationSignal); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -1929,7 +1931,8 @@ public abstract class ContentResolver implements ContentInterface { throw new FileNotFoundException("No content provider: " + uri); } fd = stableProvider.openTypedAssetFile( - mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal); + mPackageName, mAttributionTag, uri, mimeType, opts, + remoteCancellationSignal); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -2077,7 +2080,7 @@ public abstract class ContentResolver implements ContentInterface { } try { long startTime = SystemClock.uptimeMillis(); - Uri createdRow = provider.insert(mPackageName, mFeatureId, url, values, extras); + Uri createdRow = provider.insert(mPackageName, mAttributionTag, url, values, extras); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */); return createdRow; @@ -2158,7 +2161,7 @@ public abstract class ContentResolver implements ContentInterface { } try { long startTime = SystemClock.uptimeMillis(); - int rowsCreated = provider.bulkInsert(mPackageName, mFeatureId, url, values); + int rowsCreated = provider.bulkInsert(mPackageName, mAttributionTag, url, values); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */); return rowsCreated; @@ -2217,7 +2220,7 @@ public abstract class ContentResolver implements ContentInterface { } try { long startTime = SystemClock.uptimeMillis(); - int rowsDeleted = provider.delete(mPackageName, mFeatureId, url, extras); + int rowsDeleted = provider.delete(mPackageName, mAttributionTag, url, extras); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "delete", null); return rowsDeleted; @@ -2284,7 +2287,7 @@ public abstract class ContentResolver implements ContentInterface { } try { long startTime = SystemClock.uptimeMillis(); - int rowsUpdated = provider.update(mPackageName, mFeatureId, uri, values, extras); + int rowsUpdated = provider.update(mPackageName, mAttributionTag, uri, values, extras); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, uri, "update", null); return rowsUpdated; @@ -2333,7 +2336,7 @@ public abstract class ContentResolver implements ContentInterface { throw new IllegalArgumentException("Unknown authority " + authority); } try { - final Bundle res = provider.call(mPackageName, mFeatureId, authority, method, arg, + final Bundle res = provider.call(mPackageName, mAttributionTag, authority, method, arg, extras); Bundle.setDefusable(res, true); return res; @@ -3746,8 +3749,8 @@ public abstract class ContentResolver implements ContentInterface { } /** @hide */ - public @Nullable String getFeatureId() { - return mFeatureId; + public @Nullable String getAttributionTag() { + return mAttributionTag; } @UnsupportedAppUsage @@ -3757,7 +3760,7 @@ public abstract class ContentResolver implements ContentInterface { @UnsupportedAppUsage final String mPackageName; - final @Nullable String mFeatureId; + final @Nullable String mAttributionTag; final int mTargetSdkVersion; final ContentInterface mWrapped; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6cba3270042d..318ae11759db 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -71,6 +71,7 @@ import android.view.DisplayAdjustments; import android.view.View; import android.view.ViewDebug; import android.view.WindowManager; +import android.view.WindowManager.LayoutParams.WindowType; import android.view.autofill.AutofillManager.AutofillClient; import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.view.textclassifier.TextClassificationManager; @@ -814,16 +815,25 @@ public abstract class Context { } /** - * <p>Features are used in complex apps to logically separate parts of the app. E.g. a - * blogging app might also have a instant messaging app built in. + * <p>Attribution can be used in complex apps to logically separate parts of the app. E.g. a + * blogging app might also have a instant messaging app built in. In this case two separate tags + * can for used each sub-feature. * - * @return the feature id this context is for or {@code null} if this is the default - * feature. + * @return the attribution tag this context is for or {@code null} if this is the default. */ - public @Nullable String getFeatureId() { + public @Nullable String getAttributionTag() { return null; } + // TODO moltmann: Remove + /** + * @removed + */ + @Deprecated + public @Nullable String getFeatureId() { + return getAttributionTag(); + } + /** Return the full application info for this context's package. */ public abstract ApplicationInfo getApplicationInfo(); @@ -5820,24 +5830,34 @@ public abstract class Context { * @see #WALLPAPER_SERVICE * @throws IllegalArgumentException if token is invalid */ - public @NonNull Context createWindowContext(int type, @Nullable Bundle options) { + public @NonNull Context createWindowContext(@WindowType int type, @Nullable Bundle options) { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** - * Return a new Context object for the current Context but for a different feature in the app. - * Features can be used by complex apps to separate logical parts. + * Return a new Context object for the current Context but attribute to a different tag. + * In complex apps attribution tagging can be used to distinguish between separate logical + * parts. * - * @param featureId The feature id or {@code null} to create a context for the default feature. + * @param attributionTag The tag or {@code null} to create a context for the default. * - * @return A {@link Context} for the feature + * @return A {@link Context} that is tagged for the new attribution * - * @see #getFeatureId() + * @see #getAttributionTag() */ - public @NonNull Context createFeatureContext(@Nullable String featureId) { + public @NonNull Context createAttributionContext(@Nullable String attributionTag) { throw new RuntimeException("Not implemented. Must override in a subclass."); } + // TODO moltmann: remove + /** + * @removed + */ + @Deprecated + public @NonNull Context createFeatureContext(@Nullable String featureId) { + return createAttributionContext(featureId); + } + /** * Return a new Context object for the current Context but whose storage * APIs are backed by device-protected storage. diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 91d214badd2d..d389d2a4ae09 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -42,6 +42,7 @@ import android.os.Looper; import android.os.UserHandle; import android.view.Display; import android.view.DisplayAdjustments; +import android.view.WindowManager.LayoutParams.WindowType; import android.view.autofill.AutofillManager.AutofillClient; import java.io.File; @@ -163,8 +164,8 @@ public class ContextWrapper extends Context { /** @hide */ @Override - public @Nullable String getFeatureId() { - return mBase.getFeatureId(); + public @Nullable String getAttributionTag() { + return mBase.getAttributionTag(); } @Override @@ -978,13 +979,13 @@ public class ContextWrapper extends Context { @Override @NonNull - public Context createWindowContext(int type, @Nullable Bundle options) { + public Context createWindowContext(@WindowType int type, @Nullable Bundle options) { return mBase.createWindowContext(type, options); } @Override - public @NonNull Context createFeatureContext(@Nullable String featureId) { - return mBase.createFeatureContext(featureId); + public @NonNull Context createAttributionContext(@Nullable String attributionTag) { + return mBase.createAttributionContext(attributionTag); } @Override diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index 37643da375df..84b0f0e80bab 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -38,7 +38,7 @@ import java.util.ArrayList; * @hide */ public interface IContentProvider extends IInterface { - public Cursor query(String callingPkg, @Nullable String featureId, Uri url, + public Cursor query(String callingPkg, @Nullable String attributionTag, Uri url, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) throws RemoteException; @@ -59,8 +59,8 @@ public interface IContentProvider extends IInterface { throws RemoteException { return insert(callingPkg, null, url, initialValues, null); } - public Uri insert(String callingPkg, String featureId, Uri url, ContentValues initialValues, - Bundle extras) throws RemoteException; + public Uri insert(String callingPkg, String attributionTag, Uri url, + ContentValues initialValues, Bundle extras) throws RemoteException; @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " + "ContentProviderClient#bulkInsert(android.net.Uri, android.content.ContentValues[])" @@ -69,7 +69,7 @@ public interface IContentProvider extends IInterface { throws RemoteException { return bulkInsert(callingPkg, null, url, initialValues); } - public int bulkInsert(String callingPkg, String featureId, Uri url, + public int bulkInsert(String callingPkg, String attributionTag, Uri url, ContentValues[] initialValues) throws RemoteException; @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " @@ -80,7 +80,7 @@ public interface IContentProvider extends IInterface { return delete(callingPkg, null, url, ContentResolver.createSqlQueryBundle(selection, selectionArgs)); } - public int delete(String callingPkg, String featureId, Uri url, Bundle extras) + public int delete(String callingPkg, String attributionTag, Uri url, Bundle extras) throws RemoteException; @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " @@ -91,18 +91,18 @@ public interface IContentProvider extends IInterface { return update(callingPkg, null, url, values, ContentResolver.createSqlQueryBundle(selection, selectionArgs)); } - public int update(String callingPkg, String featureId, Uri url, ContentValues values, + public int update(String callingPkg, String attributionTag, Uri url, ContentValues values, Bundle extras) throws RemoteException; - public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url, - String mode, ICancellationSignal signal, IBinder callerToken) + public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag, + Uri url, String mode, ICancellationSignal signal, IBinder callerToken) throws RemoteException, FileNotFoundException; - public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId, + public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag, Uri url, String mode, ICancellationSignal signal) throws RemoteException, FileNotFoundException; - public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId, + public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String attributionTag, String authority, ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException; @@ -115,15 +115,15 @@ public interface IContentProvider extends IInterface { return call(callingPkg, null, "unknown", method, arg, extras); } - public Bundle call(String callingPkg, @Nullable String featureId, String authority, + public Bundle call(String callingPkg, @Nullable String attributionTag, String authority, String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException; - public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid, - int modeFlags) throws RemoteException; + public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri, + int uid, int modeFlags) throws RemoteException; public ICancellationSignal createCancellationSignal() throws RemoteException; - public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) + public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri) throws RemoteException; /** @@ -131,20 +131,21 @@ public interface IContentProvider extends IInterface { * call returns immediately, and the resulting type is returned when available via * a binder callback. */ - void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri, + void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri, RemoteCallback callback) throws RemoteException; - public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) + public Uri uncanonicalize(String callingPkg, @Nullable String attributionTag, Uri uri) throws RemoteException; - public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, + public boolean refresh(String callingPkg, @Nullable String attributionTag, Uri url, @Nullable Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException; // Data interchange. public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException; - public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId, - Uri url, String mimeType, Bundle opts, ICancellationSignal signal) + public AssetFileDescriptor openTypedAssetFile(String callingPkg, + @Nullable String attributionTag, Uri url, String mimeType, Bundle opts, + ICancellationSignal signal) throws RemoteException, FileNotFoundException; /* IPC constants */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 4bb7346d6416..38c1890cfc42 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -26,6 +26,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.AppGlobals; @@ -86,6 +87,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.Set; +import java.util.TimeZone; /** * An intent is an abstract description of an operation to be performed. It @@ -2312,7 +2314,8 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast Action: The timezone has changed. The intent will have the following extra values:</p> * <ul> - * <li><em>time-zone</em> - The java.util.TimeZone.getID() value identifying the new time zone.</li> + * <li>{@link #EXTRA_TIMEZONE} - The java.util.TimeZone.getID() value identifying the new + * time zone.</li> * </ul> * * <p class="note">This is a protected intent that can only be sent @@ -5785,6 +5788,14 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_TIME = "android.intent.extra.TIME"; /** + * Extra sent with {@link #ACTION_TIMEZONE_CHANGED} specifying the new time zone of the device. + * + * <p>Type: String, the same as returned by {@link TimeZone#getID()} to identify time zones. + */ + @SuppressLint("ActionValue") + public static final String EXTRA_TIMEZONE = "time-zone"; + + /** * Optional int extra for {@link #ACTION_TIME_CHANGED} that indicates the * user has set their time format preference. See {@link #EXTRA_TIME_PREF_VALUE_USE_12_HOUR}, * {@link #EXTRA_TIME_PREF_VALUE_USE_24_HOUR} and diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java index 33bd839a53f6..052c9209f6f7 100644 --- a/core/java/android/content/PermissionChecker.java +++ b/core/java/android/content/PermissionChecker.java @@ -123,7 +123,7 @@ public final class PermissionChecker { * @param uid The uid for which to check. * @param packageName The package name for which to check. If null the * the first package for the calling UID will be used. - * @param featureId Feature in the package + * @param attributionTag attribution tag * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}. * @param message A message describing the reason the permission was checked @@ -133,9 +133,9 @@ public final class PermissionChecker { @PermissionResult public static int checkPermissionForDataDelivery(@NonNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, - @Nullable String featureId, @Nullable String message) { - return checkPermissionCommon(context, permission, pid, uid, packageName, featureId, message, - true /*forDataDelivery*/); + @Nullable String attributionTag, @Nullable String message) { + return checkPermissionCommon(context, permission, pid, uid, packageName, attributionTag, + message, true /*forDataDelivery*/); } /** @@ -171,8 +171,8 @@ public final class PermissionChecker { @PermissionResult public static int checkPermissionForPreflight(@NonNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName) { - return checkPermissionCommon(context, permission, pid, uid, packageName, null /*featureId*/, - null /*message*/, false /*forDataDelivery*/); + return checkPermissionCommon(context, permission, pid, uid, packageName, + null /*attributionTag*/, null /*message*/, false /*forDataDelivery*/); } /** @@ -207,7 +207,7 @@ public final class PermissionChecker { public static int checkSelfPermissionForDataDelivery(@NonNull Context context, @NonNull String permission, @Nullable String message) { return checkPermissionForDataDelivery(context, permission, Process.myPid(), - Process.myUid(), context.getPackageName(), context.getFeatureId(), message); + Process.myUid(), context.getPackageName(), context.getAttributionTag(), message); } /** @@ -266,7 +266,7 @@ public final class PermissionChecker { * @param permission The permission to check. * @param packageName The package name making the IPC. If null the * the first package for the calling UID will be used. - * @param featureId The feature inside of the app + * @param attributionTag attribution tag * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}. * @param message A message describing the reason the permission was checked @@ -276,12 +276,12 @@ public final class PermissionChecker { @PermissionResult public static int checkCallingPermissionForDataDelivery(@NonNull Context context, @NonNull String permission, @Nullable String packageName, - @Nullable String featureId, @Nullable String message) { + @Nullable String attributionTag, @Nullable String message) { if (Binder.getCallingPid() == Process.myPid()) { return PERMISSION_HARD_DENIED; } return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(), - Binder.getCallingUid(), packageName, featureId, message); + Binder.getCallingUid(), packageName, attributionTag, message); } /** @@ -343,20 +343,20 @@ public final class PermissionChecker { * @param permission The permission to check. * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}. - * @param featureId feature Id of caller (if not self) + * @param attributionTag attribution tag of caller (if not self) * @param message A message describing the reason the permission was checked * * @see #checkCallingOrSelfPermissionForPreflight(Context, String) */ @PermissionResult public static int checkCallingOrSelfPermissionForDataDelivery(@NonNull Context context, - @NonNull String permission, @Nullable String featureId, @Nullable String message) { + @NonNull String permission, @Nullable String attributionTag, @Nullable String message) { String packageName = (Binder.getCallingPid() == Process.myPid()) ? context.getPackageName() : null; - featureId = (Binder.getCallingPid() == Process.myPid()) - ? context.getFeatureId() : featureId; + attributionTag = (Binder.getCallingPid() == Process.myPid()) + ? context.getAttributionTag() : attributionTag; return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(), - Binder.getCallingUid(), packageName, featureId, message); + Binder.getCallingUid(), packageName, attributionTag, message); } /** @@ -395,7 +395,7 @@ public final class PermissionChecker { } static int checkPermissionCommon(@NonNull Context context, @NonNull String permission, - int pid, int uid, @Nullable String packageName, @Nullable String featureId, + int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery) { final PermissionInfo permissionInfo; try { @@ -414,18 +414,18 @@ public final class PermissionChecker { } if (permissionInfo.isAppOp()) { - return checkAppOpPermission(context, permission, pid, uid, packageName, featureId, + return checkAppOpPermission(context, permission, pid, uid, packageName, attributionTag, message, forDataDelivery); } if (permissionInfo.isRuntime()) { - return checkRuntimePermission(context, permission, pid, uid, packageName, featureId, - message, forDataDelivery); + return checkRuntimePermission(context, permission, pid, uid, packageName, + attributionTag, message, forDataDelivery); } return context.checkPermission(permission, pid, uid); } private static int checkAppOpPermission(@NonNull Context context, @NonNull String permission, - int pid, int uid, @Nullable String packageName, @Nullable String featureId, + int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery) { final String op = AppOpsManager.permissionToOp(permission); if (op == null || packageName == null) { @@ -434,7 +434,7 @@ public final class PermissionChecker { final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); final int opMode = (forDataDelivery) - ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, featureId, message) + ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message) : appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName); switch (opMode) { @@ -453,7 +453,7 @@ public final class PermissionChecker { } private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission, - int pid, int uid, @Nullable String packageName, @Nullable String featureId, + int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery) { if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) { return PERMISSION_HARD_DENIED; @@ -466,7 +466,7 @@ public final class PermissionChecker { final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); final int opMode = (forDataDelivery) - ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, featureId, message) + ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message) : appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName); if (opMode == AppOpsManager.MODE_ALLOWED) { diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 0b2b5b1f0ec4..f25ce76d9365 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1222,13 +1222,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { dest.writeInt(lockTaskLaunchMode); if (windowLayout != null) { dest.writeInt(1); - dest.writeInt(windowLayout.width); - dest.writeFloat(windowLayout.widthFraction); - dest.writeInt(windowLayout.height); - dest.writeFloat(windowLayout.heightFraction); - dest.writeInt(windowLayout.gravity); - dest.writeInt(windowLayout.minWidth); - dest.writeInt(windowLayout.minHeight); + windowLayout.writeToParcel(dest); } else { dest.writeInt(0); } @@ -1372,8 +1366,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @attr ref android.R.styleable#AndroidManifestLayout_minHeight */ public static final class WindowLayout { - public WindowLayout(int width, float widthFraction, int height, float heightFraction, int gravity, - int minWidth, int minHeight) { + public WindowLayout(int width, float widthFraction, int height, float heightFraction, + int gravity, int minWidth, int minHeight) { this.width = width; this.widthFraction = widthFraction; this.height = height; @@ -1392,6 +1386,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { gravity = source.readInt(); minWidth = source.readInt(); minHeight = source.readInt(); + windowLayoutAffinity = source.readString(); } /** @@ -1458,11 +1453,30 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public final int minHeight; /** + * Affinity of window layout parameters. Activities with the same UID and window layout + * affinity will share the same window dimension record. + * @hide + */ + public String windowLayoutAffinity; + + /** * Returns if this {@link WindowLayout} has specified bounds. * @hide */ public boolean hasSpecifiedSize() { return width >= 0 || height >= 0 || widthFraction >= 0 || heightFraction >= 0; } + + /** @hide */ + public void writeToParcel(Parcel dest) { + dest.writeInt(width); + dest.writeFloat(widthFraction); + dest.writeInt(height); + dest.writeFloat(heightFraction); + dest.writeInt(gravity); + dest.writeInt(minWidth); + dest.writeInt(minHeight); + dest.writeString(windowLayoutAffinity); + } } } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index a15afe04201b..c82fffa4aa00 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -25,6 +25,7 @@ import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ProcessInfo; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Build; @@ -38,6 +39,8 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.Parcelling; +import com.android.internal.util.Parcelling.BuiltIn.ForBoolean; import com.android.server.SystemConfig; import java.lang.annotation.Retention; @@ -56,7 +59,8 @@ import java.util.UUID; * <application> tag. */ public class ApplicationInfo extends PackageItemInfo implements Parcelable { - + private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class); + /** * Default task affinity of all activities in this application. See * {@link ActivityInfo#taskAffinity} for more information. This comes @@ -1273,6 +1277,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public String zygotePreloadName; /** + * Indicates if the application has requested GWP-ASan to be enabled, disabled, or left + * unspecified. Processes can override this setting. + * @hide + */ + @Nullable + public Boolean enableGwpAsan; + + /** * Represents the default policy. The actual policy used will depend on other properties of * the application, e.g. the target SDK version. * @hide @@ -1413,6 +1425,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { pw.println(prefix + "usesNonSdkApi=" + usesNonSdkApi()); pw.println(prefix + "allowsPlaybackCapture=" + (isAudioPlaybackCaptureAllowed() ? "true" : "false")); + if (enableGwpAsan != null) { + pw.println(prefix + "enableGwpAsan=" + enableGwpAsan); + } } super.dumpBack(pw, prefix); } @@ -1511,6 +1526,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { if (category != CATEGORY_UNDEFINED) { proto.write(ApplicationInfoProto.Detail.CATEGORY, category); } + if (enableGwpAsan != null) { + proto.write(ApplicationInfoProto.Detail.ENABLE_GWP_ASAN, enableGwpAsan); + } proto.end(detailToken); } proto.end(token); @@ -1620,6 +1638,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { mHiddenApiPolicy = orig.mHiddenApiPolicy; hiddenUntilInstalled = orig.hiddenUntilInstalled; zygotePreloadName = orig.zygotePreloadName; + enableGwpAsan = orig.enableGwpAsan; } public String toString() { @@ -1703,6 +1722,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(mHiddenApiPolicy); dest.writeInt(hiddenUntilInstalled ? 1 : 0); dest.writeString(zygotePreloadName); + sForBoolean.parcel(enableGwpAsan, dest, parcelableFlags); } public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR @@ -1783,6 +1803,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { mHiddenApiPolicy = source.readInt(); hiddenUntilInstalled = source.readInt() != 0; zygotePreloadName = source.readString(); + enableGwpAsan = sForBoolean.unparcel(source); } /** @@ -2161,6 +2182,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** {@hide} */ public void setResourcePath(String resourcePath) { scanPublicSourceDir = resourcePath; } /** {@hide} */ public void setBaseResourcePath(String baseResourcePath) { publicSourceDir = baseResourcePath; } /** {@hide} */ public void setSplitResourcePaths(String[] splitResourcePaths) { splitPublicSourceDirs = splitResourcePaths; } + /** {@hide} */ public void setGwpAsanEnabled(@Nullable Boolean value) { enableGwpAsan = value; } /** {@hide} */ @UnsupportedAppUsage @@ -2172,4 +2194,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { @UnsupportedAppUsage public String getBaseResourcePath() { return publicSourceDir; } /** {@hide} */ public String[] getSplitResourcePaths() { return splitPublicSourceDirs; } + @Nullable + public Boolean isGwpAsanEnabled() { return enableGwpAsan; } } diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 3261cb124e00..dc3a02976416 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -95,7 +96,7 @@ public class CrossProfileApps { mService.startActivityAsUser( mContext.getIApplicationThread(), mContext.getPackageName(), - mContext.getFeatureId(), + mContext.getAttributionTag(), component, targetUser.getIdentifier(), true); @@ -128,14 +129,44 @@ public class CrossProfileApps { @NonNull Intent intent, @NonNull UserHandle targetUser, @Nullable Activity callingActivity) { + startActivity(intent, targetUser, callingActivity, /* options= */ null); + } + + /** + * Starts the specified activity of the caller package in the specified profile. + * + * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}, + * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code + * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and + * target user profiles must be in the same profile group. The target user must be a valid user + * returned from {@link #getTargetUserProfiles()}. + * + * @param intent The intent to launch. A component in the caller package must be specified. + * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by + * {@link #getTargetUserProfiles()} if different to the calling user, otherwise a + * {@link SecurityException} will be thrown. + * @param callingActivity The activity to start the new activity from for the purposes of + * deciding which task the new activity should belong to. If {@code null}, the activity + * will always be started in a new task. + * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}. + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.INTERACT_ACROSS_PROFILES, + android.Manifest.permission.INTERACT_ACROSS_USERS}) + public void startActivity( + @NonNull Intent intent, + @NonNull UserHandle targetUser, + @Nullable Activity callingActivity, + @Nullable Bundle options) { try { mService.startActivityAsUserByIntent( mContext.getIApplicationThread(), mContext.getPackageName(), - mContext.getFeatureId(), + mContext.getAttributionTag(), intent, targetUser.getIdentifier(), - callingActivity != null ? callingActivity.getActivityToken() : null); + callingActivity != null ? callingActivity.getActivityToken() : null, + options); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -159,7 +190,7 @@ public class CrossProfileApps { public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) { try { mService.startActivityAsUser(mContext.getIApplicationThread(), - mContext.getPackageName(), mContext.getFeatureId(), component, + mContext.getPackageName(), mContext.getAttributionTag(), component, targetUser.getIdentifier(), false); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl index 5a6e008608f3..4cecb30990e6 100644 --- a/core/java/android/content/pm/ICrossProfileApps.aidl +++ b/core/java/android/content/pm/ICrossProfileApps.aidl @@ -31,7 +31,8 @@ interface ICrossProfileApps { in String callingFeatureId, in ComponentName component, int userId, boolean launchMainActivity); void startActivityAsUserByIntent(in IApplicationThread caller, in String callingPackage, - in String callingFeatureId, in Intent intent, int userId, in IBinder callingActivity); + in String callingFeatureId, in Intent intent, int userId, in IBinder callingActivity, + in Bundle options); List<UserHandle> getTargetUserProfiles(in String callingPackage); boolean canInteractAcrossProfiles(in String callingPackage); boolean canRequestInteractAcrossProfiles(in String callingPackage); diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java index c0fdcc900577..a45bf7930509 100644 --- a/core/java/android/content/pm/InstallSourceInfo.java +++ b/core/java/android/content/pm/InstallSourceInfo.java @@ -66,7 +66,18 @@ public final class InstallSourceInfo implements Parcelable { mInstallingPackageName = source.readString(); } - /** The name of the package that requested the installation, or null if not available. */ + /** + * The name of the package that requested the installation, or null if not available. + * + * This is normally the same as the installing package name. If the installing package name + * is changed, for example by calling + * {@link PackageManager#setInstallerPackageName(String, String)}, the initiating package name + * remains unchanged. It continues to identify the actual package that performed the install + * or update. + * <p> + * Null may be returned if the app was not installed by a package (e.g. a system app or an app + * installed via adb) or if the initiating package has itself been uninstalled. + */ @Nullable public String getInitiatingPackageName() { return mInitiatingPackageName; @@ -100,9 +111,11 @@ public final class InstallSourceInfo implements Parcelable { /** * The name of the package responsible for the installation (the installer of record), or null * if not available. - * Note that this may differ from the initiating package name and can be modified. - * - * @see PackageManager#setInstallerPackageName(String, String) + * Note that this may differ from the initiating package name and can be modified via + * {@link PackageManager#setInstallerPackageName(String, String)}. + * <p> + * Null may be returned if the app was not installed by a package (e.g. a system app or an app + * installed via adb) or if the installing package has itself been uninstalled. */ @Nullable public String getInstallingPackageName() { diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 86242fd6f82f..4e4897f59ad3 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -721,7 +721,7 @@ public class LauncherApps { } try { mService.startActivityAsUser(mContext.getIApplicationThread(), - mContext.getPackageName(), mContext.getFeatureId(), + mContext.getPackageName(), mContext.getAttributionTag(), component, sourceBounds, opts, user); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); @@ -739,8 +739,8 @@ public class LauncherApps { @Nullable Rect sourceBounds, @Nullable Bundle opts) { try { mService.startSessionDetailsActivityAsUser(mContext.getIApplicationThread(), - mContext.getPackageName(), mContext.getFeatureId(), sessionInfo, sourceBounds, - opts, sessionInfo.getUser()); + mContext.getPackageName(), mContext.getAttributionTag(), sessionInfo, + sourceBounds, opts, sessionInfo.getUser()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -760,7 +760,7 @@ public class LauncherApps { logErrorForInvalidProfileAccess(user); try { mService.showAppDetailsAsUser(mContext.getIApplicationThread(), - mContext.getPackageName(), mContext.getFeatureId(), + mContext.getPackageName(), mContext.getAttributionTag(), component, sourceBounds, opts, user); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 1f5317679bd2..ec3590fd23cf 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -2323,6 +2323,7 @@ public class PackageInstaller { /** * Get the value set in {@link SessionParams#setOriginatingUri(Uri)}. + * Note: This value will only be non-null for the owner of the session. */ public @Nullable Uri getOriginatingUri() { return originatingUri; @@ -2337,6 +2338,7 @@ public class PackageInstaller { /** * Get the value set in {@link SessionParams#setReferrerUri(Uri)} + * Note: This value will only be non-null for the owner of the session. */ public @Nullable Uri getReferrerUri() { return referrerUri; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 7600a08a256c..b0964fa398a7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3411,12 +3411,13 @@ public abstract class PackageManager { public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 17; /** - * Permission flags: Reserved for use by the permission controller. - * + * Permission flags: Reserved for use by the permission controller. The platform and any + * packages besides the permission controller should not assume any definition about these + * flags. * @hide */ @SystemApi - public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = 1 << 28 | 1 << 29 + public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = 1 << 28 | 1 << 29 | 1 << 30 | 1 << 31; /** diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index c6875a4b3443..18f13431c09a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -55,7 +55,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.parsing.ParsingPackageUtils; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; @@ -190,7 +189,7 @@ public class PackageParser { public static final String TAG_OVERLAY = "overlay"; public static final String TAG_PACKAGE = "package"; public static final String TAG_PACKAGE_VERIFIER = "package-verifier"; - public static final String TAG_FEATURE = "feature"; + public static final String TAG_ATTRIBUTION = "attribution"; public static final String TAG_PERMISSION = "permission"; public static final String TAG_PERMISSION_GROUP = "permission-group"; public static final String TAG_PERMISSION_TREE = "permission-tree"; @@ -209,6 +208,8 @@ public class PackageParser { public static final String TAG_USES_SPLIT = "uses-split"; public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect"; + public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY = + "android.activity_window_layout_affinity"; /** * Bit mask of all the valid bits that can be set in recreateOnConfigChanges. @@ -4560,6 +4561,8 @@ public class PackageParser { } } + resolveWindowLayout(a); + if (!setExported) { a.info.exported = a.intents.size() > 0; } @@ -4726,6 +4729,35 @@ public class PackageParser { height, heightFraction, gravity, minWidth, minHeight); } + /** + * Resolves values in {@link ActivityInfo.WindowLayout}. + * + * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in + * Android R and some variants of pre-R. + */ + private void resolveWindowLayout(Activity activity) { + // There isn't a metadata for us to fall back. Whatever is in layout is correct. + if (activity.metaData == null + || !activity.metaData.containsKey(METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) { + return; + } + + final ActivityInfo aInfo = activity.info; + // Layout already specifies a value. We should just use that one. + if (aInfo.windowLayout != null && aInfo.windowLayout.windowLayoutAffinity != null) { + return; + } + + String windowLayoutAffinity = activity.metaData.getString( + METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY); + if (aInfo.windowLayout == null) { + aInfo.windowLayout = new ActivityInfo.WindowLayout(-1 /* width */, + -1 /* widthFraction */, -1 /* height */, -1 /* heightFraction */, + Gravity.NO_GRAVITY, -1 /* minWidth */, -1 /* minHeight */); + } + aInfo.windowLayout.windowLayoutAffinity = windowLayoutAffinity; + } + private Activity parseActivityAlias(Package owner, Resources res, XmlResourceParser parser, int flags, String[] outError, CachedComponentArgs cachedArgs) diff --git a/core/java/android/content/pm/ProcessInfo.java b/core/java/android/content/pm/ProcessInfo.java index c77a267958f5..a3730672b988 100644 --- a/core/java/android/content/pm/ProcessInfo.java +++ b/core/java/android/content/pm/ProcessInfo.java @@ -23,66 +23,157 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.ArraySet; +import com.android.internal.util.DataClass; +import com.android.internal.util.Parcelling; + /** * Information about a process an app may run. This corresponds to information collected from the * AndroidManifest.xml's <permission-group> tags. * @hide */ +@DataClass(genGetters = true, genSetters = false, genParcelable = true, genAidl = false, + genBuilder = false) public class ProcessInfo implements Parcelable { /** * The name of the process, fully-qualified based on the app's package name. */ + @NonNull public String name; /** * If non-null, these are permissions that are not allowed in this process. */ @Nullable + @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringArraySet.class) public ArraySet<String> deniedPermissions; - public ProcessInfo(String name, ArraySet<String> deniedPermissions) { - this.name = name; - this.deniedPermissions = deniedPermissions; - } + /** + * Indicates if the process has requested GWP-ASan to be enabled, disabled, or left unspecified. + */ + @Nullable + public Boolean enableGwpAsan; @Deprecated public ProcessInfo(@NonNull ProcessInfo orig) { this.name = orig.name; this.deniedPermissions = orig.deniedPermissions; + this.enableGwpAsan = orig.enableGwpAsan; } - public int describeContents() { - return 0; + + + // Code below generated by codegen v1.0.15. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ProcessInfo.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new ProcessInfo. + * + * @param name + * The name of the process, fully-qualified based on the app's package name. + * @param deniedPermissions + * If non-null, these are permissions that are not allowed in this process. + * @param enableGwpAsan + * Indicates if the process has requested GWP-ASan to be enabled, disabled, or left unspecified. + */ + @DataClass.Generated.Member + public ProcessInfo( + @NonNull String name, + @Nullable ArraySet<String> deniedPermissions, + @Nullable Boolean enableGwpAsan) { + this.name = name; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, name); + this.deniedPermissions = deniedPermissions; + this.enableGwpAsan = enableGwpAsan; + + // onConstructed(); // You can define this method to get a callback } - public void writeToParcel(Parcel dest, int parcelableFlags) { - dest.writeString(this.name); - final int numDenied = this.deniedPermissions != null - ? this.deniedPermissions.size() : 0; - dest.writeInt(numDenied); - for (int i = 0; i < numDenied; i++) { - dest.writeString(this.deniedPermissions.valueAt(i)); + @DataClass.Generated.Member + static Parcelling<ArraySet<String>> sParcellingForDeniedPermissions = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForInternedStringArraySet.class); + static { + if (sParcellingForDeniedPermissions == null) { + sParcellingForDeniedPermissions = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForInternedStringArraySet()); } } - public static final @NonNull Creator<ProcessInfo> CREATOR = - new Creator<ProcessInfo>() { - public ProcessInfo createFromParcel(Parcel source) { - return new ProcessInfo(source); - } - public ProcessInfo[] newArray(int size) { - return new ProcessInfo[size]; - } - }; - - private ProcessInfo(Parcel source) { - this.name = source.readString(); - final int numDenied = source.readInt(); - if (numDenied > 0) { - this.deniedPermissions = new ArraySet<>(numDenied); - for (int i = numDenied - 1; i >= 0; i--) { - this.deniedPermissions.add(TextUtils.safeIntern(source.readString())); - } - } + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (deniedPermissions != null) flg |= 0x2; + if (enableGwpAsan != null) flg |= 0x4; + dest.writeByte(flg); + dest.writeString(name); + sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags); + if (enableGwpAsan != null) dest.writeBoolean(enableGwpAsan); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected ProcessInfo(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + String _name = in.readString(); + ArraySet<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in); + Boolean _enableGwpAsan = (flg & 0x4) == 0 ? null : (Boolean) in.readBoolean(); + + this.name = _name; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, name); + this.deniedPermissions = _deniedPermissions; + this.enableGwpAsan = _enableGwpAsan; + + // onConstructed(); // You can define this method to get a callback } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<ProcessInfo> CREATOR + = new Parcelable.Creator<ProcessInfo>() { + @Override + public ProcessInfo[] newArray(int size) { + return new ProcessInfo[size]; + } + + @Override + public ProcessInfo createFromParcel(@NonNull Parcel in) { + return new ProcessInfo(in); + } + }; + + @DataClass.Generated( + time = 1582840056156L, + codegenVersion = "1.0.15", + sourceFile = "frameworks/base/core/java/android/content/pm/ProcessInfo.java", + inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.annotation.Nullable java.lang.Boolean enableGwpAsan\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + } diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 49e8c052cbca..af875787bba6 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -1558,11 +1558,6 @@ public final class ShortcutInfo implements Parcelable { * "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each * {@link #getActivity} for each of the two types of shortcuts (static and dynamic). * - * <p>Because static shortcuts and dynamic shortcuts have overlapping ranks, - * when a launcher app shows shortcuts for an activity, it should first show - * the static shortcuts, followed by the dynamic shortcuts. Within each of those categories, - * shortcuts should be sorted by rank in ascending order. - * * <p><em>Floating shortcuts</em>, or shortcuts that are neither static nor dynamic, will all * have rank 0, because they aren't sorted. * diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index aa93d80fbfeb..1e02a7d0d3cf 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -24,7 +24,7 @@ import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageParser; import android.content.pm.parsing.component.ParsedActivity; -import android.content.pm.parsing.component.ParsedFeature; +import android.content.pm.parsing.component.ParsedAttribution; import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedIntentInfo; import android.content.pm.parsing.component.ParsedPermission; @@ -77,7 +77,7 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage addProvider(ParsedProvider parsedProvider); - ParsingPackage addFeature(ParsedFeature permission); + ParsingPackage addAttribution(ParsedAttribution attribution); ParsingPackage addReceiver(ParsedActivity parsedReceiver); @@ -236,6 +236,8 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage setEnabled(boolean enabled); + ParsingPackage setGwpAsanEnabled(Boolean enableGwpAsan); + ParsingPackage setCrossProfile(boolean crossProfile); ParsingPackage setFullBackupContent(int fullBackupContent); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index a9b72d041f8b..d7c0dfbc65e2 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -30,9 +30,10 @@ import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageParser; +import android.content.pm.ProcessInfo; import android.content.pm.parsing.component.ParsedActivity; +import android.content.pm.parsing.component.ParsedAttribution; import android.content.pm.parsing.component.ParsedComponent; -import android.content.pm.parsing.component.ParsedFeature; import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedIntentInfo; import android.content.pm.parsing.component.ParsedMainComponent; @@ -243,7 +244,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { protected List<ParsedProvider> providers = emptyList(); @NonNull - private List<ParsedFeature> features = emptyList(); + private List<ParsedAttribution> attributions = emptyList(); @NonNull protected List<ParsedPermission> permissions = emptyList(); @@ -406,6 +407,10 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { private boolean allowNativeHeapPointerTagging; private boolean preserveLegacyExternalStorage; + @Nullable + @DataClass.ParcelWith(ForBoolean.class) + protected Boolean enableGwpAsan = null; + // TODO(chiuwinson): Non-null @Nullable private ArraySet<String> mimeGroups; @@ -620,8 +625,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override - public ParsingPackageImpl addFeature(ParsedFeature feature) { - this.features = CollectionUtils.add(this.features, feature); + public ParsingPackageImpl addAttribution(ParsedAttribution attribution) { + this.attributions = CollectionUtils.add(this.attributions, attribution); return this; } @@ -904,7 +909,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { appInfo.volumeUuid = volumeUuid; appInfo.zygotePreloadName = zygotePreloadName; appInfo.crossProfile = isCrossProfile(); - + appInfo.setGwpAsanEnabled(enableGwpAsan); appInfo.setBaseCodePath(baseCodePath); appInfo.setBaseResourcePath(baseCodePath); appInfo.setCodePath(codePath); @@ -990,7 +995,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeTypedList(this.receivers); dest.writeTypedList(this.services); dest.writeTypedList(this.providers); - dest.writeTypedList(this.features); + dest.writeTypedList(this.attributions); dest.writeTypedList(this.permissions); dest.writeTypedList(this.permissionGroups); dest.writeTypedList(this.instrumentations); @@ -1086,6 +1091,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeBoolean(this.allowNativeHeapPointerTagging); dest.writeBoolean(this.preserveLegacyExternalStorage); dest.writeArraySet(this.mimeGroups); + sForBoolean.parcel(this.enableGwpAsan, dest, flags); } public ParsingPackageImpl(Parcel in) { @@ -1149,7 +1155,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR); this.services = in.createTypedArrayList(ParsedService.CREATOR); this.providers = in.createTypedArrayList(ParsedProvider.CREATOR); - this.features = in.createTypedArrayList(ParsedFeature.CREATOR); + this.attributions = in.createTypedArrayList(ParsedAttribution.CREATOR); this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR); this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR); this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR); @@ -1243,6 +1249,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.allowNativeHeapPointerTagging = in.readBoolean(); this.preserveLegacyExternalStorage = in.readBoolean(); this.mimeGroups = (ArraySet<String>) in.readArraySet(boot); + this.enableGwpAsan = sForBoolean.unparcel(in); } public static final Parcelable.Creator<ParsingPackageImpl> CREATOR = @@ -1509,8 +1516,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @NonNull @Override - public List<ParsedFeature> getFeatures() { - return features; + public List<ParsedAttribution> getAttributions() { + return attributions; } @NonNull @@ -1965,6 +1972,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override + @Nullable + public Boolean isGwpAsanEnabled() { + return enableGwpAsan; + } + + @Override public boolean isPartiallyDirectBootAware() { return partiallyDirectBootAware; } @@ -2420,6 +2433,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override + public ParsingPackageImpl setGwpAsanEnabled(@Nullable Boolean value) { + enableGwpAsan = value; + return this; + } + + @Override public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) { partiallyDirectBootAware = value; return this; diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index 048b924e10cc..0b673b5f89cd 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -28,7 +28,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageParser; import android.content.pm.ServiceInfo; import android.content.pm.parsing.component.ParsedActivity; -import android.content.pm.parsing.component.ParsedFeature; +import android.content.pm.parsing.component.ParsedAttribution; import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedIntentInfo; import android.content.pm.parsing.component.ParsedPermission; @@ -80,7 +80,7 @@ public interface ParsingPackageRead extends Parcelable { List<ConfigurationInfo> getConfigPreferences(); @NonNull - List<ParsedFeature> getFeatures(); + List<ParsedAttribution> getAttributions(); /** * @see PackageInfo#featureGroups @@ -840,6 +840,13 @@ public interface ParsingPackageRead extends Parcelable { @Nullable Set<String> getMimeGroups(); + /** + * @see ApplicationInfo#enableGwpAsan + * @see R.styleable#AndroidManifest_enableGwpAsan + */ + @Nullable + public Boolean isGwpAsanEnabled(); + // TODO(b/135203078): Hide and enforce going through PackageInfoUtils ApplicationInfo toAppInfoWithoutState(); } diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index b4f21593165f..3ed0fd57975b 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -40,6 +40,7 @@ import android.content.pm.ConfigurationInfo; import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; @@ -48,8 +49,8 @@ import android.content.pm.Signature; import android.content.pm.parsing.component.ComponentParseUtils; import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedActivityUtils; -import android.content.pm.parsing.component.ParsedFeature; -import android.content.pm.parsing.component.ParsedFeatureUtils; +import android.content.pm.parsing.component.ParsedAttribution; +import android.content.pm.parsing.component.ParsedAttributionUtils; import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedInstrumentationUtils; import android.content.pm.parsing.component.ParsedIntentInfo; @@ -672,7 +673,7 @@ public class ParsingPackageUtils { ); } - if (!ParsedFeature.isCombinationValid(pkg.getFeatures())) { + if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) { return input.error( INSTALL_PARSE_FAILED_BAD_MANIFEST, "Combination <feature> tags are not valid" @@ -707,8 +708,9 @@ public class ParsingPackageUtils { return parseOverlay(input, pkg, res, parser); case PackageParser.TAG_KEY_SETS: return parseKeySets(input, pkg, res, parser); - case PackageParser.TAG_FEATURE: - return parseFeature(input, pkg, res, parser); + case "feature": // TODO moltmann: Remove + case PackageParser.TAG_ATTRIBUTION: + return parseAttribution(input, pkg, res, parser); case PackageParser.TAG_PERMISSION_GROUP: return parsePermissionGroup(input, pkg, res, parser); case PackageParser.TAG_PERMISSION: @@ -917,13 +919,15 @@ public class ParsingPackageUtils { return input.success(pkg); } - private static ParseResult<ParsingPackage> parseFeature(ParseInput input, ParsingPackage pkg, - Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { - ParseResult<ParsedFeature> result = ParsedFeatureUtils.parseFeature(res, parser, input); + private static ParseResult<ParsingPackage> parseAttribution(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws IOException, XmlPullParserException { + ParseResult<ParsedAttribution> result = ParsedAttributionUtils.parseAttribution(res, + parser, input); if (result.isError()) { return input.error(result); } - return input.success(pkg.addFeature(result.getResult())); + return input.success(pkg.addAttribution(result.getResult())); } private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input, @@ -1660,6 +1664,11 @@ public class ParsingPackageUtils { && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) { return input.error("Invalid class loader name: " + classLoaderName); } + + if (sa.hasValue(R.styleable.AndroidManifestApplication_enableGwpAsan)) { + pkg.setGwpAsanEnabled( + sa.getBoolean(R.styleable.AndroidManifestApplication_enableGwpAsan, false)); + } } finally { sa.recycle(); } diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java index d32171dfe962..4c93d0950388 100644 --- a/core/java/android/content/pm/parsing/component/ParsedActivity.java +++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java @@ -285,14 +285,8 @@ public class ParsedActivity extends ParsedMainComponent { dest.writeBundle(this.metaData); if (windowLayout != null) { - dest.writeBoolean(true); - dest.writeInt(windowLayout.width); - dest.writeFloat(windowLayout.widthFraction); - dest.writeInt(windowLayout.height); - dest.writeFloat(windowLayout.heightFraction); - dest.writeInt(windowLayout.gravity); - dest.writeInt(windowLayout.minWidth); - dest.writeInt(windowLayout.minHeight); + dest.writeInt(1); + windowLayout.writeToParcel(dest); } else { dest.writeBoolean(false); } diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java index 1dcf262d46ba..6e5c51a22025 100644 --- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java @@ -17,16 +17,17 @@ package android.content.pm.parsing.component; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - import static android.content.pm.parsing.component.ComponentParseUtils.flag; import android.annotation.NonNull; import android.app.ActivityTaskManager; import android.content.pm.ActivityInfo; import android.content.pm.PackageParser; - import android.content.pm.parsing.ParsingPackage; +import android.content.pm.parsing.ParsingPackageUtils; import android.content.pm.parsing.ParsingUtils; +import android.content.pm.parsing.result.ParseInput; +import android.content.pm.parsing.result.ParseResult; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; @@ -42,9 +43,6 @@ import android.view.WindowManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; -import android.content.pm.parsing.ParsingPackageUtils; -import android.content.pm.parsing.result.ParseInput; -import android.content.pm.parsing.result.ParseResult; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -379,6 +377,12 @@ public class ParsedActivityUtils { } } + ParseResult<ActivityInfo.WindowLayout> layoutResult = resolveWindowLayout(activity, input); + if (layoutResult.isError()) { + return input.error(layoutResult); + } + activity.windowLayout = layoutResult.getResult(); + if (!setExported) { activity.exported = activity.getIntents().size() > 0; } @@ -481,4 +485,35 @@ public class ParsedActivityUtils { sw.recycle(); } } + + /** + * Resolves values in {@link ActivityInfo.WindowLayout}. + * + * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in + * Android R and some variants of pre-R. + */ + private static ParseResult<ActivityInfo.WindowLayout> resolveWindowLayout( + ParsedActivity activity, ParseInput input) { + // There isn't a metadata for us to fall back. Whatever is in layout is correct. + if (activity.metaData == null || !activity.metaData.containsKey( + PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) { + return input.success(activity.windowLayout); + } + + // Layout already specifies a value. We should just use that one. + if (activity.windowLayout != null && activity.windowLayout.windowLayoutAffinity != null) { + return input.success(activity.windowLayout); + } + + String windowLayoutAffinity = activity.metaData.getString( + PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY); + ActivityInfo.WindowLayout layout = activity.windowLayout; + if (layout == null) { + layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */, + -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY, + -1 /* minWidth */, -1 /* minHeight */); + } + layout.windowLayoutAffinity = windowLayoutAffinity; + return input.success(layout); + } } diff --git a/core/java/android/content/pm/parsing/component/ParsedFeature.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java index b8a9098cfa0e..02b3c7d9c909 100644 --- a/core/java/android/content/pm/parsing/component/ParsedFeature.java +++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java @@ -29,65 +29,65 @@ import java.util.ArrayList; import java.util.List; /** - * A {@link android.R.styleable#AndroidManifestFeature <feature>} tag parsed from the + * A {@link android.R.styleable#AndroidManifestAttribution <attribution>} tag parsed from the * manifest. * * @hide */ @DataClass(genAidl = false) -public class ParsedFeature implements Parcelable { - /** Maximum length of featureId */ - public static final int MAX_FEATURE_ID_LEN = 50; +public class ParsedAttribution implements Parcelable { + /** Maximum length of attribution tag */ + public static final int MAX_ATTRIBUTION_TAG_LEN = 50; - /** Maximum amount of features per package */ - private static final int MAX_NUM_FEATURES = 1000; + /** Maximum amount of attributions per package */ + private static final int MAX_NUM_ATTRIBUTIONS = 1000; - /** Id of the feature */ - public final @NonNull String id; + /** Tag of the attribution */ + public final @NonNull String tag; - /** User visible label fo the feature */ + /** User visible label fo the attribution */ public final @StringRes int label; - /** Ids of previously declared features this feature inherits from */ + /** Ids of previously declared attributions this attribution inherits from */ public final @NonNull List<String> inheritFrom; /** - * @return Is this set of features a valid combination for a single package? + * @return Is this set of attributions a valid combination for a single package? */ - public static boolean isCombinationValid(@Nullable List<ParsedFeature> features) { - if (features == null) { + public static boolean isCombinationValid(@Nullable List<ParsedAttribution> attributions) { + if (attributions == null) { return true; } - ArraySet<String> featureIds = new ArraySet<>(features.size()); - ArraySet<String> inheritFromFeatureIds = new ArraySet<>(); + ArraySet<String> attributionTags = new ArraySet<>(attributions.size()); + ArraySet<String> inheritFromAttributionTags = new ArraySet<>(); - int numFeatures = features.size(); - if (numFeatures > MAX_NUM_FEATURES) { + int numAttributions = attributions.size(); + if (numAttributions > MAX_NUM_ATTRIBUTIONS) { return false; } - for (int featureNum = 0; featureNum < numFeatures; featureNum++) { - boolean wasAdded = featureIds.add(features.get(featureNum).id); + for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) { + boolean wasAdded = attributionTags.add(attributions.get(attributionNum).tag); if (!wasAdded) { // feature id is not unique return false; } } - for (int featureNum = 0; featureNum < numFeatures; featureNum++) { - ParsedFeature feature = features.get(featureNum); + for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) { + ParsedAttribution feature = attributions.get(attributionNum); int numInheritFrom = feature.inheritFrom.size(); for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) { String inheritFrom = feature.inheritFrom.get(inheritFromNum); - if (featureIds.contains(inheritFrom)) { - // Cannot inherit from a feature that is still defined + if (attributionTags.contains(inheritFrom)) { + // Cannot inherit from a attribution that is still defined return false; } - boolean wasAdded = inheritFromFeatureIds.add(inheritFrom); + boolean wasAdded = inheritFromAttributionTags.add(inheritFrom); if (!wasAdded) { // inheritFrom is not unique return false; @@ -106,7 +106,7 @@ public class ParsedFeature implements Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedFeature.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -114,8 +114,8 @@ public class ParsedFeature implements Parcelable { @android.annotation.IntDef(prefix = "MAX_", value = { - MAX_FEATURE_ID_LEN, - MAX_NUM_FEATURES + MAX_ATTRIBUTION_TAG_LEN, + MAX_NUM_ATTRIBUTIONS }) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @DataClass.Generated.Member @@ -124,32 +124,32 @@ public class ParsedFeature implements Parcelable { @DataClass.Generated.Member public static String maxToString(@Max int value) { switch (value) { - case MAX_FEATURE_ID_LEN: - return "MAX_FEATURE_ID_LEN"; - case MAX_NUM_FEATURES: - return "MAX_NUM_FEATURES"; + case MAX_ATTRIBUTION_TAG_LEN: + return "MAX_ATTRIBUTION_TAG_LEN"; + case MAX_NUM_ATTRIBUTIONS: + return "MAX_NUM_ATTRIBUTIONS"; default: return Integer.toHexString(value); } } /** - * Creates a new ParsedFeature. + * Creates a new ParsedAttribution. * - * @param id - * Id of the feature + * @param tag + * Tag of the attribution * @param label - * User visible label fo the feature + * User visible label fo the attribution * @param inheritFrom - * Ids of previously declared features this feature inherits from + * Ids of previously declared attributions this attribution inherits from */ @DataClass.Generated.Member - public ParsedFeature( - @NonNull String id, + public ParsedAttribution( + @NonNull String tag, @StringRes int label, @NonNull List<String> inheritFrom) { - this.id = id; + this.tag = tag; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, id); + NonNull.class, null, tag); this.label = label; com.android.internal.util.AnnotationValidations.validate( StringRes.class, null, label); @@ -166,7 +166,7 @@ public class ParsedFeature implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - dest.writeString(id); + dest.writeString(tag); dest.writeInt(label); dest.writeStringList(inheritFrom); } @@ -178,18 +178,18 @@ public class ParsedFeature implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - protected ParsedFeature(@NonNull Parcel in) { + protected ParsedAttribution(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } - String _id = in.readString(); + String _tag = in.readString(); int _label = in.readInt(); List<String> _inheritFrom = new ArrayList<>(); in.readStringList(_inheritFrom); - this.id = _id; + this.tag = _tag; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, id); + NonNull.class, null, tag); this.label = _label; com.android.internal.util.AnnotationValidations.validate( StringRes.class, null, label); @@ -201,24 +201,24 @@ public class ParsedFeature implements Parcelable { } @DataClass.Generated.Member - public static final @NonNull Parcelable.Creator<ParsedFeature> CREATOR - = new Parcelable.Creator<ParsedFeature>() { + public static final @NonNull Parcelable.Creator<ParsedAttribution> CREATOR + = new Parcelable.Creator<ParsedAttribution>() { @Override - public ParsedFeature[] newArray(int size) { - return new ParsedFeature[size]; + public ParsedAttribution[] newArray(int size) { + return new ParsedAttribution[size]; } @Override - public ParsedFeature createFromParcel(@NonNull Parcel in) { - return new ParsedFeature(in); + public ParsedAttribution createFromParcel(@NonNull Parcel in) { + return new ParsedAttribution(in); } }; @DataClass.Generated( - time = 1581379861853L, + time = 1583436566499L, codegenVersion = "1.0.14", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedFeature.java", - inputSignatures = "public static final int MAX_FEATURE_ID_LEN\nprivate static final int MAX_NUM_FEATURES\npublic final @android.annotation.NonNull java.lang.String id\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedFeature>)\nclass ParsedFeature extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)") + sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java", + inputSignatures = "public static final int MAX_ATTRIBUTION_TAG_LEN\nprivate static final int MAX_NUM_ATTRIBUTIONS\npublic final @android.annotation.NonNull java.lang.String tag\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java b/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java index fb5280175d1a..c4b1a0eb90a0 100644 --- a/core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java @@ -34,34 +34,40 @@ import java.util.Collections; import java.util.List; /** @hide */ -public class ParsedFeatureUtils { +public class ParsedAttributionUtils { @NonNull - public static ParseResult<ParsedFeature> parseFeature(Resources res, XmlResourceParser parser, - ParseInput input) throws IOException, XmlPullParserException { - String featureId; + public static ParseResult<ParsedAttribution> parseAttribution(Resources res, + XmlResourceParser parser, ParseInput input) + throws IOException, XmlPullParserException { + String attributionTag; int label; List<String> inheritFrom = null; - TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeature); + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAttribution); if (sa == null) { - return input.error("<feature> could not be parsed"); + return input.error("<attribution> could not be parsed"); } try { - featureId = sa.getNonConfigurationString(R.styleable.AndroidManifestFeature_featureId, - 0); - if (featureId == null) { - return input.error("<featureId> does not specify android:featureId"); + attributionTag = sa.getNonConfigurationString( + R.styleable.AndroidManifestAttribution_tag, 0); + if (attributionTag == null) { + // TODO moltmann: Remove handling of featureId + attributionTag = sa.getNonConfigurationString( + R.styleable.AndroidManifestAttribution_featureId, 0); + if (attributionTag == null) { + return input.error("<attribution> does not specify android:tag"); + } } - if (featureId.length() > ParsedFeature.MAX_FEATURE_ID_LEN) { - return input.error("<featureId> is too long. Max length is " - + ParsedFeature.MAX_FEATURE_ID_LEN); + if (attributionTag.length() > ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN) { + return input.error("android:tag is too long. Max length is " + + ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN); } - label = sa.getResourceId(R.styleable.AndroidManifestFeature_label, 0); + label = sa.getResourceId(R.styleable.AndroidManifestAttribution_label, 0); if (label == Resources.ID_NULL) { - return input.error("<featureId> does not specify android:label"); + return input.error("<attribution> does not specify android:label"); } } finally { sa.recycle(); @@ -77,14 +83,15 @@ public class ParsedFeatureUtils { String tagName = parser.getName(); if (tagName.equals("inherit-from")) { - sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeatureInheritFrom); + sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestAttributionInheritFrom); if (sa == null) { return input.error("<inherit-from> could not be parsed"); } try { String inheritFromId = sa.getNonConfigurationString( - R.styleable.AndroidManifestFeatureInheritFrom_featureId,0); + R.styleable.AndroidManifestAttributionInheritFrom_tag, 0); if (inheritFrom == null) { inheritFrom = new ArrayList<>(); @@ -94,7 +101,7 @@ public class ParsedFeatureUtils { sa.recycle(); } } else { - return input.error("Bad element under <feature>: " + tagName); + return input.error("Bad element under <attribution>: " + tagName); } } @@ -104,6 +111,6 @@ public class ParsedFeatureUtils { ((ArrayList) inheritFrom).trimToSize(); } - return input.success(new ParsedFeature(featureId, label, inheritFrom)); + return input.success(new ParsedAttribution(attributionTag, label, inheritFrom)); } } diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java index da7bf984aa7f..3b6020a72469 100644 --- a/core/java/android/content/pm/parsing/component/ParsedProcess.java +++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java @@ -41,6 +41,9 @@ public class ParsedProcess implements Parcelable { @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class) protected Set<String> deniedPermissions = emptySet(); + @Nullable + protected Boolean enableGwpAsan = null; + public ParsedProcess() { } @@ -71,13 +74,15 @@ public class ParsedProcess implements Parcelable { @DataClass.Generated.Member public ParsedProcess( @NonNull String name, - @NonNull Set<String> deniedPermissions) { + @NonNull Set<String> deniedPermissions, + @Nullable Boolean enableGwpAsan) { this.name = name; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, name); this.deniedPermissions = deniedPermissions; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, deniedPermissions); + this.enableGwpAsan = enableGwpAsan; // onConstructed(); // You can define this method to get a callback } @@ -93,6 +98,11 @@ public class ParsedProcess implements Parcelable { } @DataClass.Generated.Member + public @Nullable Boolean getEnableGwpAsan() { + return enableGwpAsan; + } + + @DataClass.Generated.Member static Parcelling<Set<String>> sParcellingForDeniedPermissions = Parcelling.Cache.get( Parcelling.BuiltIn.ForInternedStringSet.class); @@ -109,8 +119,12 @@ public class ParsedProcess implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } + byte flg = 0; + if (enableGwpAsan != null) flg |= 0x4; + dest.writeByte(flg); dest.writeString(name); sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags); + if (enableGwpAsan != null) dest.writeBoolean(enableGwpAsan); } @Override @@ -124,8 +138,10 @@ public class ParsedProcess implements Parcelable { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } + byte flg = in.readByte(); String _name = in.readString(); Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in); + Boolean _enableGwpAsan = (flg & 0x4) == 0 ? null : (Boolean) in.readBoolean(); this.name = _name; com.android.internal.util.AnnotationValidations.validate( @@ -133,6 +149,7 @@ public class ParsedProcess implements Parcelable { this.deniedPermissions = _deniedPermissions; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, deniedPermissions); + this.enableGwpAsan = _enableGwpAsan; // onConstructed(); // You can define this method to get a callback } @@ -152,10 +169,10 @@ public class ParsedProcess implements Parcelable { }; @DataClass.Generated( - time = 1581452315946L, + time = 1582589960479L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java", - inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)") + inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected @android.annotation.Nullable java.lang.Boolean enableGwpAsan\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java index 48250666a58b..3820790cc2b1 100644 --- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java @@ -103,6 +103,11 @@ public class ParsedProcessUtils { if (proc.name == null || proc.name.length() <= 0) { return input.error("<process> does not specify android:process"); } + + if (sa.hasValue(R.styleable.AndroidManifestProcess_enableGwpAsan)) { + proc.enableGwpAsan = + sa.getBoolean(R.styleable.AndroidManifestProcess_enableGwpAsan, false); + } } finally { sa.recycle(); } diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index a091f84fe463..972b0f55acef 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -486,7 +486,7 @@ public final class CameraManager { "Camera service is currently unavailable"); } cameraUser = cameraService.connectDevice(callbacks, cameraId, - mContext.getOpPackageName(), mContext.getFeatureId(), uid); + mContext.getOpPackageName(), mContext.getAttributionTag(), uid); } else { // Use legacy camera implementation for HAL1 devices int id; diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java index c5cb8037d4db..e90b57cdc7b4 100644 --- a/core/java/android/hardware/lights/Light.java +++ b/core/java/android/hardware/lights/Light.java @@ -37,7 +37,8 @@ public final class Light implements Parcelable { /** * Creates a new light with the given data. * - * @hide */ + * @hide + */ public Light(int id, int ordinal, int type) { mId = id; mOrdinal = ordinal; @@ -76,8 +77,24 @@ public final class Light implements Parcelable { } }; + @Override + public boolean equals(Object obj) { + if (obj instanceof Light) { + Light light = (Light) obj; + return mId == light.mId && mOrdinal == light.mOrdinal && mType == light.mType; + } + return false; + } + + @Override + public int hashCode() { + return mId; + } + /** * Returns the id of the light. + * + * <p>This is an opaque value used as a unique identifier for the light. */ public int getId() { return mId; @@ -86,11 +103,9 @@ public final class Light implements Parcelable { /** * Returns the ordinal of the light. * - * <p>This represents the physical order of the lights on the device. The exact values are - * device-dependent, but for example, if there are lights in a row, sorting the Light objects - * by ordinal should match the order in which they appear on the device. If the device has - * 4 lights, the ordinals could be [1, 2, 3, 4] or [0, 10, 20, 30] or any other values that - * have the same sort order. + * <p>This is a sort key that represents the physical order of lights on the device with the + * same type. In the case of multiple lights arranged in a line, for example, the ordinals + * could be [1, 2, 3, 4], or [0, 10, 20, 30], or any other values that have the same sort order. */ public int getOrdinal() { return mOrdinal; diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java index 1bc051b977a8..8cd231224472 100644 --- a/core/java/android/hardware/lights/LightsManager.java +++ b/core/java/android/hardware/lights/LightsManager.java @@ -161,7 +161,7 @@ public final class LightsManager { * @param request the settings for lights that should change */ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - public void setLights(@NonNull LightsRequest request) { + public void requestLights(@NonNull LightsRequest request) { Preconditions.checkNotNull(request); if (!mClosed) { try { diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java index a36da4c7d85d..5c4fc6707e96 100644 --- a/core/java/android/hardware/lights/LightsRequest.java +++ b/core/java/android/hardware/lights/LightsRequest.java @@ -86,7 +86,7 @@ public final class LightsRequest { * Create a LightsRequest object used to override lights on the device. * * <p>The generated {@link LightsRequest} should be used in - * {@link LightsManager.Session#setLights(LightsLightsRequest). + * {@link LightsManager.Session#requestLights(LightsLightsRequest). */ public @NonNull LightsRequest build() { return new LightsRequest(mChanges); diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java index bf641d72eab1..1aeb76a396b4 100644 --- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java +++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java @@ -28,7 +28,6 @@ import android.content.res.XmlResourceParser; import android.text.TextUtils; import android.util.ArraySet; import android.util.AttributeSet; -import android.util.Log; import android.util.Slog; import android.util.Xml; @@ -231,15 +230,7 @@ public class KeyphraseEnrollmentInfo { com.android.internal.R.styleable.VoiceEnrollmentApplication); keyphraseMetadata = getKeyphraseFromTypedArray(array, packageName, parseErrors); array.recycle(); - } catch (XmlPullParserException e) { - String error = "Error parsing keyphrase enrollment meta-data for " + packageName; - parseErrors.add(error + ": " + e); - Slog.w(TAG, error, e); - } catch (IOException e) { - String error = "Error parsing keyphrase enrollment meta-data for " + packageName; - parseErrors.add(error + ": " + e); - Slog.w(TAG, error, e); - } catch (PackageManager.NameNotFoundException e) { + } catch (XmlPullParserException | PackageManager.NameNotFoundException | IOException e) { String error = "Error parsing keyphrase enrollment meta-data for " + packageName; parseErrors.add(error + ": " + e); Slog.w(TAG, error, e); @@ -390,7 +381,6 @@ public class KeyphraseEnrollmentInfo { * False if not. */ public boolean isUidSupportedEnrollmentApplication(int uid) { - Log.d(TAG, "isUidSupportedEnrollmentApplication: " + toString()); return mEnrollmentApplicationUids.contains(uid); } diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index 0513feef801f..6efd03c44b9f 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -28,6 +28,7 @@ import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.View; import android.view.WindowManager; import java.lang.annotation.Retention; @@ -94,6 +95,13 @@ public class SoftInputWindow extends Dialog { lp.token = token; getWindow().setAttributes(lp); updateWindowState(SoftInputWindowState.TOKEN_SET); + + // As soon as we have a token, make sure the window is added (but not shown) by + // setting visibility to INVISIBLE and calling show() on Dialog. Note that + // WindowInsetsController.OnControllableInsetsChangedListener relies on the window + // being added to function. + getWindow().getDecorView().setVisibility(View.INVISIBLE); + show(); return; case SoftInputWindowState.TOKEN_SET: case SoftInputWindowState.SHOWN_AT_LEAST_ONCE: diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java index 9c999b202797..590fbb36a0e8 100644 --- a/core/java/android/os/ConfigUpdate.java +++ b/core/java/android/os/ConfigUpdate.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; /** @@ -114,20 +115,36 @@ public final class ConfigUpdate { = "android.os.action.UPDATE_CARRIER_ID_DB"; /** - * Broadcast intent action indicating that the updated emergency number database is available. - * <p>Extra: "VERSION" the numeric version of the new data. Devices should only install if the - * update version is newer than the current one. - * <p>Extra: "REQUIRED_HASH" the hash of the current update data. - * <p>Input: {@link android.content.Intent#getData} is URI of downloaded emergency number file. - * Devices should pick up the downloaded file and persist to the database - * {@code com.android.internal.telephony.emergency.EmergencyNumberTracker}. + * Update the emergency number database into the devices. + * <p>Extra: {@link #EXTRA_VERSION} the numeric version of the database. + * <p>Extra: {@link #EXTRA_REQUIRED_HASH} the hash of the database. + * <p>Input: {@link android.content.Intent#getData} the URI to download emergency number + * database. * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.UPDATE_CONFIG) public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB"; + /** + * An integer to indicate the numeric version of the new data. Devices should only install + * if the update version is newer than the current one. + * + * @hide + */ + @SystemApi + public static final String EXTRA_VERSION = "android.os.extra.VERSION"; + + /** + * A string to indicate the hash of the data. + * + * @hide + */ + @SystemApi + public static final String EXTRA_REQUIRED_HASH = "android.os.extra.REQUIRED_HASH"; + private ConfigUpdate() { } } diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index b478dbe2555d..a7e326378228 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -77,6 +77,7 @@ public final class Looper { @UnsupportedAppUsage final MessageQueue mQueue; final Thread mThread; + private boolean mInLoop; @UnsupportedAppUsage private Printer mLogging; @@ -155,6 +156,12 @@ public final class Looper { if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } + if (me.mInLoop) { + Slog.w(TAG, "Loop again would have the queued messages be executed" + + " before this one completed."); + } + + me.mInLoop = true; final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, diff --git a/core/java/android/os/Users.md b/core/java/android/os/Users.md new file mode 100644 index 000000000000..3bbbe5452fd3 --- /dev/null +++ b/core/java/android/os/Users.md @@ -0,0 +1,109 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License + --> + +# Users for system developers + +## Concepts + +### User + +A user of a device e.g. usually a human being. Each user has its own home screen. + +#### User Profile + +A user can have multiple profiles. E.g. one for the private life and one for work. Each profile +has a different set of apps and accounts but they share one home screen. All profiles of a +profile group can be active at the same time. + +Each profile has a separate [`userId`](#int-userid). Unless needed user profiles are treated as +completely separate users. + +#### Profile Group + +All user profiles that share a home screen. You can list the profiles of a user via +`UserManager#getEnabledProfiles` (you usually don't deal with disabled profiles) + +#### Foreground user vs background user + +Only a single user profile group can be in the foreground. This is the user profile the user +currently interacts with. + +#### Parent user (profile) + +The main profile of a profile group, usually the personal (as opposed to work) profile. Get this via +`UserManager#getProfileParent` (returns `null` if the user does not have profiles) + +#### Managed user (profile) + +The other profiles of a profile group. The name comes from the fact that these profiles are usually +managed by a device policy controller app. You can create a managed profile from within the device +policy controller app on your phone. + +#### Account + +An account of a user profile with a (usually internet based) service. E.g. aname@gmail.com or +aname@yahoo.com. Each profile can have multiple accounts. A profile does not have to have a +account. + +## Data types + +### int userId + +... usually marked as `@UserIdInt` + +The id of a user profile. List all users via `adb shell dumpsys user`. There is no data type for a +user, all you can do is using the user id of the parent profile as a proxy for the user. + +### int uid + +Identity of an app. This is the same as a Linux uid, but in Android there is one uid per package, +per user. + +It is highly discouraged, but uids can be shared between multiple packages using the +`android:sharedUserId` manifest attribute. + +### class UserHandle + +A wrapper for userId. Used esp. in public APIs instead of `int userId` as it clearly distinguishes +from uid. + +## Security model + +Multiple packages can share an uid by using `android:sharedUserId` manifest attribute. If packages +share a uid they can run in the same process via `android:process` manifest attribute. Further file +level access is also tracked by uid. Hence any security or privacy mechanism needs to be built on +a uid granularity. + +On the other hand apps belonging to the same user cannot see each others files. They can only +interact via activity launches, broadcasts, providers, and service bindings. All of them can be be +protected by [permissions](../permission/Permissions.md). Hence any new general communication +mechanism should be access controlled by permissions. + +## Lifecycle + +A system service should deal with users being started and stopped by overriding +`SystemService.onSwitchUser` and `SystemService.onStopUser`. + +If users profiles become inactive the system should stop all apps of this profile from interacting +with other apps or the system. + +Another important lifecycle event is `onUnlockUser`. Only for unlocked user profiles you can access +all data, e.g. which packages are installed. + +You only want to deal with user profiles that + +- are in the profile group of the foreground user +- the user profile is unlocked and not yet stopped diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index 0483514e6297..f01139542541 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -42,6 +42,6 @@ oneway interface IPermissionController { void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, String packageName, String permission, int grantState, in AndroidFuture callback); void grantOrUpgradeDefaultRuntimePermissions(in AndroidFuture callback); - void updateUserSensitive(in AndroidFuture callback); void notifyOneTimePermissionSessionTimeout(String packageName); + void updateUserSensitiveForApp(int uid, in AndroidFuture callback); } diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 2a1857fd0027..f08e3d25632b 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -46,6 +46,7 @@ import android.content.pm.ResolveInfo; import android.os.Binder; import android.os.Bundle; import android.os.Handler; +import android.os.Process; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; @@ -626,14 +627,26 @@ public final class PermissionControllerManager { } /** - * @see PermissionControllerService#onUpdateUserSensitive() + * @see PermissionControllerManager#updateUserSensitiveForApp * @hide */ public void updateUserSensitive() { + updateUserSensitiveForApp(Process.INVALID_UID); + } + + /** + * @see PermissionControllerService#onUpdateUserSensitiveForApp + * @hide + */ + public void updateUserSensitiveForApp(int uid) { mRemoteService.postAsync(service -> { AndroidFuture<Void> future = new AndroidFuture<>(); - service.updateUserSensitive(future); + service.updateUserSensitiveForApp(uid, future); return future; + }).whenComplete((res, err) -> { + if (err != null) { + Log.e(TAG, "Error updating user_sensitive flags for uid " + uid, err); + } }); } diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 263b2c7a4ac7..4a42230ad15a 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -218,11 +218,14 @@ public abstract class PermissionControllerService extends Service { * Called by system to update the * {@link PackageManager}{@code .FLAG_PERMISSION_USER_SENSITIVE_WHEN_*} flags for permissions. * <p> - * This is typically when creating a new user or upgrading either system or - * permission controller package. + * + * If uid is -1, updates the permission flags for all packages. + * + * Typically called by the system when a new app is installed or updated or when creating a + * new user or upgrading either system or permission controller package. */ @BinderThread - public void onUpdateUserSensitivePermissionFlags() { + public void onUpdateUserSensitivePermissionFlags(int uid, @NonNull Runnable callback) { throw new AbstractMethodError("Must be overridden in implementing class"); } @@ -459,11 +462,14 @@ public abstract class PermissionControllerService extends Service { } @Override - public void updateUserSensitive(AndroidFuture callback) { + public void updateUserSensitiveForApp(int uid, @NonNull AndroidFuture callback) { Preconditions.checkNotNull(callback, "callback cannot be null"); - onUpdateUserSensitivePermissionFlags(); - callback.complete(null); + try { + onUpdateUserSensitivePermissionFlags(uid, () -> callback.complete(null)); + } catch (Exception e) { + callback.completeExceptionally(e); + } } @Override diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md new file mode 100644 index 000000000000..e62bd0a88bf6 --- /dev/null +++ b/core/java/android/permission/Permissions.md @@ -0,0 +1,832 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License + --> + +# Android permissions for system developers + +This document targets system developers. App developers should refer to the [public + documentation](https://developer.android.com/guide/topics/permissions/overview). + +## Definitions + +Each app (often called package) has a unique name called package name. Each package has a manifest +file describing properties of the package. The android system server is in a special package named +"android". + +When a package gets installed the package is (usually) assigned a unique identifier called [uid +](../os/Users.md#int-uid). +This is the same as a uid in Linux, but this often leads to confusion. It is easiest to see a uid +as a unique identifier for a package. + +Usually an app is running in a container called a process. Each process has a unique id called +pid, but unlike the uid the pid changes each time the process is restarted and app that are not +currently running don't have a pid. The process container makes sure that other apps cannot +negatively interact with an app. Processes can only interact via controlled interactions called +remote procedure calls (RPCs). Android’s RPC mechanism is called _Binder_. + +As no app code can be trusted the permission need to be checked on the receiving side of the +Binder call. + +For more details please take a look at [Android's security model](../os/Users.md#security-model). + +## Permissions for regular apps + +### Install time permissions + +The purpose of install time permissions is to control access to APIs where it does not makes sense +to involve the user. This can be either because the API is not sensitive, or because additional +checks exist. + +#### Defining a permission + +Any package can define a permission. For that it simply adds an entry in the manifest file +`<permission android:name="com.example.myapp.myfirstpermission" />` + +Any package can do this, including the system package. When talking about [permissions for system + apps](#permissions-for-system-apps) we will see that it is important which package defines a +permission. + +It is common good practice to prefix the permission name with the package name to avoid collisions. + +#### Requesting a permission + +Any app can request any permission via adding an entry in the manifest file like +`<uses-permission android:name="com.example.myapp.myfirstpermission" />` + +A requested permission does not necessarily mean that the permission is granted. When and how a +permission is granted depends on the protection level of the permission. If no protection level is +set, the permission will always be granted. Such "normal" permissions can still be useful as it +will be easy to find apps using a certain functionality on app stores and by checking `dumpsys +package`. + +#### Checking a permission + +`Context.checkPermission(permission, pid, uid)` returns if the pid/uid has the permission. By far +the most common case is to check the permission on the receiving end of a binder call. In this case +the pid can be read as `Binder.callingPid()` and the uid as `Binder.callingUid()`. The uid is a +mandatory argument as permissions are maintained per uid. The pid can be set to -1 +if not pid is available. The context class contains handy wrappers for `checkPermission`, such as +`enforeCallingPermission` which calls checkPermission with `Binder.callingPid`/`Binder.callingUid` +and throws a SecurityException when the permission is not granted. + +#### Verifying an app has an install time permission + +In `dumpsys package my.package.name` there are two sections. In requested permissions all +permissions of the `uses-permission` tags are listed. In install permission the permissions with +their grant state are listed. If an install time permission is not listed here, it is not granted. + +``` +Packages: + Package [com.android.packageinstaller] (2eb7062): + userId=10071 + [...] + requested permissions: + android.permission.MANAGE_USERS + android.permission.INSTALL_PACKAGES + android.permission.DELETE_PACKAGES + android.permission.READ_INSTALL_SESSIONS + android.permission.RECEIVE_BOOT_COMPLETED + android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS + android.permission.USE_RESERVED_DISK + android.permission.UPDATE_APP_OPS_STATS + android.permission.MANAGE_APP_OPS_MODES + android.permission.INTERACT_ACROSS_USERS_FULL + android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME + android.permission.PACKAGE_USAGE_STATS + install permissions: + android.permission.USE_RESERVED_DISK: granted=true + android.permission.INSTALL_PACKAGES: granted=true + android.permission.RECEIVE_BOOT_COMPLETED: granted=true + android.permission.INTERACT_ACROSS_USERS_FULL: granted=true + android.permission.PACKAGE_USAGE_STATS: granted=true + android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME: granted=true + android.permission.READ_INSTALL_SESSIONS: granted=true + android.permission.MANAGE_USERS: granted=true + android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS: granted=true + android.permission.MANAGE_APP_OPS_MODES: granted=true + android.permission.UPDATE_APP_OPS_STATS: granted=true + android.permission.DELETE_PACKAGES: granted=true +``` + +#### End-to-end: Protecting an RPC call via a permission + +##### Service Manifest + +```xml +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.example.myservice"> + <!-- Define a permission --> + <permission android:name="com.android.example.myservice.MY_PERMISSION" /> + <application> + <service android:name=".MyService" android:exported="true" /> + </application> +</manifest> +``` + +##### Service code + +```kotlin +class MyService : Service() { + override fun onBind(intent: Intent?): IBinder? { + return object : IMyService.Stub() { + override fun doSomething() { + // Verify that calling UID has the permission + enforceCallingPermission( + "com.android.example.myservice.MY_PERMISSION", + "Need to hold permission" + ) + // do something + } + }.asBinder() + } +} +``` + +##### Caller Manifest + +```xml +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.example.myapp"> + <!-- request a permission --> + <uses-permission android:name="com.android.example.myservice.MY_PERMISSION" /> + <application /> +</manifest> +``` + +### Runtime permissions + +Runtime permission must be granted by the user during runtime. This is needed if the API protects +data or functionality that is sensitive for the user. E.g. the users current location is protected +by a runtime permission. + +Users want a system that is secure and privacy focused by default. User can also often not make a +good choice when asked at the wrong time without enough context. Hence in general runtime +permissions should be avoided and the API should be built in a way where no private data needs to be +leaked. + +#### Defining a runtime permission + +Runtime permissions are defined in the same way as install time permissions. To tag them as runtime +permissions the `protectionLevel` needs to be set to dangerous. Dangerous is a synonym for +runtime permissions in the Android platform. + +```xml +<uses-permission android:name="com.example.myapp.myfirstruntimepermission" + android:protectionLevel="dangerous" /> +``` + +#### Requesting a runtime permission + +Similar to install time permissions any app can request a runtime permission by adding the +`<uses-permission android:name="com.example.myapp.myfirstruntimepermission" />` +to the manifest. + +By default runtime permissions are not granted. The app needs to call `Activity.requestPermissions` +during runtime to ask the user for the permission. The user might then grant or deny and once the +decision is made the activity is called by via `Activity.onPermissionGranted`. + +During development and testing a runtime permission can be granted via the `pm` shell command or by +using the `UiAutomator.grantRuntimePermission` API call. Please note that this does _not_ grant the +[app-op](#runtime-permissions-and-app-ops) synchronously. Unless the app needs to test the actual +permission grant flow it is recommended to grant the runtime permissions during install using +`adb install -g /my/package.apk`. + +#### Checking a runtime permission + +For runtime permissions defined by a 3rd party apps it is fine to check a runtime +permission like an install time permission. For system defined permissions you need to check all +runtime permissions by using the `PermissionChecker` utility. It is good practice to use the tool +anywhere possible. + +The permission checker might return `PERMISSION_DENIED_APP_OP` which should lead to a silent +failure. This can only happen for system defined runtime permissions. + +##### Runtime permissions and app-ops + +> See [App-ops](../app/AppOps.md). + +The PermissionChecker code fundamentally looks like this: + +```kotlin +class PermissionChecker { + fun checkCallingPermission(context: Context, permission: String) { + if (isRuntimePermission(permission)) { + if (context.checkPermission(uid, permission) == DENIED) { + return PERMISSION_DENIED + } + + val appOpOfPermission = AppOpsManager.permissionToOp(permission) + if (appOpOfPermission == null) { + // not platform defined + return PERMISSION_GRANTED + } + + val appOpMode = appOpsManager.noteOp(appOpOfPermission) + if (appOpMode == AppOpsManager.MODE_ALLOWED) { + return PERMISSION_GRANTED + } else { + return PERMISSION_DENIED_APP_OP + } + } else { + return PERMISSION_DENIED + } + } +} +``` + +For each platform defined runtime permission there is a matching app-op. When calling +`AppOpsManager.noteOp` this returns either `MODE_ALLOWED` or `MODE_IGNORED`. + +This value is then used to decide between `PERMISSION_DENIED_APP_OP` and `PERMISSION_GRANTED`. + +The primary purpose of the special `PERMISSION_DENIED_APP_OP` state was to support apps targeting an +SDK lower than 23. These apps do not understand the concept of denied runtime permissions. Hence +they would crash when getting a `SecurityException`. To protect the users' privacy while still not +crashing the app the special `PERMISSION_DENIED_APP_OP` mandates that the API should somehow +silently fail. + +A secondary use case of the `AppOpsManager.noteOp` calls is to +[track](../app/AppOps.md#Appops-for-tracking) which apps perform what runtime protected actions. + +#### Verifying an app has a runtime time permission + +In `dumpsys package my.package.name` the runtime permissions are listed per uid. I.e. different +users might have different runtime permission grants and shared uids share a grant-set. If a runtime +permission is listed as requested but not in the runtime permission section it is in it’s initial +state, i.e. not granted. + +``` +Packages: + Package [com.google.android.GoogleCamera] (ccb6af): + userId=10181 + [...] + requested permissions: + android.permission.ACCESS_COARSE_LOCATION + android.permission.ACCESS_FINE_LOCATION + android.permission.ACCESS_NETWORK_STATE + android.permission.ACCESS_NOTIFICATION_POLICY + android.permission.ACCESS_WIFI_STATE + android.permission.BIND_WALLPAPER + android.permission.CAMERA + android.permission.CHANGE_WIFI_STATE + android.permission.INTERNET + android.permission.GET_PACKAGE_SIZE + android.permission.NFC + android.permission.READ_SYNC_SETTINGS + android.permission.RECEIVE_BOOT_COMPLETED + android.permission.RECORD_AUDIO + android.permission.SET_WALLPAPER + android.permission.USE_CREDENTIALS + android.permission.VIBRATE + android.permission.WAKE_LOCK + android.permission.WRITE_EXTERNAL_STORAGE [ ... ] + android.permission.WRITE_SETTINGS + android.permission.WRITE_SYNC_SETTINGS + com.google.android.elmyra.permission.CONFIGURE_ASSIST_GESTURE + com.google.android.providers.gsf.permission.READ_GSERVICES + android.permission.FOREGROUND_SERVICE + com.google.android.googlequicksearchbox.permission.LENSVIEW_BROADCAST + android.permission.READ_EXTERNAL_STORAGE [ ... ] + [...] + User 0: [ ... ] + overlay paths: + runtime permissions: + android.permission.ACCESS_FINE_LOCATION: granted=false [ ... ] + android.permission.READ_EXTERNAL_STORAGE: granted=true [ ... ] + android.permission.ACCESS_COARSE_LOCATION: granted=false [ ... ] + android.permission.CAMERA: granted=true [ ... ] + android.permission.WRITE_EXTERNAL_STORAGE: granted=true [ ... ] + android.permission.RECORD_AUDIO: granted=true[ ... ] +``` + +#### End-to-end: Protecting an RPC call via a runtime permission + +##### Service Manifest + +```xml +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.example.myservice"> + <!-- Define a runtime permission --> + <permission android:name="com.android.example.myservice.MY_RUNTIME_PERMISSION" + android:protectionLevel="dangerous" /> + <application> + <service android:name=".MyService" android:exported="true" /> + </application> +</manifest> +``` + +##### Service code + +```kotlin +class MyService : Service() { + override fun onBind(intent: Intent?): IBinder? { + return object : IMyService.Stub() { + override fun doSomething(callingPackage: String?, callingFeatureId: String?) { + Objects.requireNonNull(callingPackageName) + + // Verify that calling UID has the permission + when (run { + PermissionChecker.checkCallingPermission( + this@MyService, + "com.android.example.myservice.MY_RUNTIME_PERMISSION", + callingPackageName, + callingFeatureId, + "Did something" + ) + }) { + PERMISSION_GRANTED -> /* do something */ + PERMISSION_DENIED_APP_OP -> /* silent failure, do nothing */ + else -> throw SecurityException( + "Cannot do something as caller is missing " + + "com.android.example.myservice.MY_RUNTIME_PERMISSION" + ) + } + } + }.asBinder() + } +} +``` + +##### Caller Manifest + +```xml +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.example.myapp"> + <!-- request a permission --> + <uses-permission android:name="com.android.example.myservice.MY_RUNTIME_PERMISSION" /> + <application /> +</manifest> +``` + +##### Caller code + +```kotlin +class MyActivity : Activity { + fun callDoSomething() { + if (checkSelfPermission("com.android.example.myservice.MY_RUNTIME_PERMISSION") == PERMISSION_DENIED) { + // Interrupt operation and request permission + requestPermissions(arrayOf("com.android.example.myservice.MY_RUNTIME_PERMISSION"), 23) + } else { + myService.doSomething(this@MyActivity.opPackageName, this@MyActivity.featureId) + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array<String>, + grantResults: IntArray + ) { + if (requestCode == 23 && grantResults[0] == PERMISSION_GRANTED) { + // Finish operation + callDoSomething() + } + } +} +``` + +#### Restricted permissions + +Some runtime permissions are restricted. They are annotated in the platforms `AndroidManifest.xml` +has `hardRestricted` or `softRestricted`. + +Restricted permissions behave uncommon when not whitelisted. When whitelisted the permissions +behave normally. What uncommon means depends on the whether they are hard or soft restricted. + +They can either be whitelisted during upgrade P->Q, but the system or need to be whitelisted by the +installer via `PackageInstaller.SessionParams.setWhitelistedRestrictedPermissions`. If this method +is not used all permissions will be whitelisted. + +Afterwards the app that originally installed the app can change the whitelisting state via +`PackageManager.addWhitelistedRestrictedPermission` and +`PackageManager.removeWhitelistedRestrictedPermission`. + +The system tracks the source of the whitelisting by having three different flags + `RESTRICTION_SYSTEM_EXEMPT`, `RESTRICTION_UPGRADE_EXEMPT`, and `RESTRICTION_INSTALLER_EXEMPT`, + +The flags can be checked in `dumpsys package my.package.name` + +``` +User 0: + [...] + runtime permissions: + android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ RESTRICTION_UPGRADE_EXEMPT ] + android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ RESTRICTION_SYSTEM_EXEMPT|RESTRICTION_UPGRADE_EXEMPT ] +``` + +##### Hard restricted + +Hard restricted permissions need to be whitelisted to be grant-able. + +##### Soft restricted + +The behavior of non-whitelisted soft restricted permissions is not uniform. The behavior is +defined in the `SoftRestrictedPermissionPolicy`. + +#### System fixed permission + +Some runtime permissions are required for normal operation of the device. In this case the system +can grant the permission as `SYSTEM_FIXED`. In this case the permission can be seen in the +[permission management settings](#settings) but cannot be revoked by the user. + +The flag can be checked in `dumpsys package my.package.name` +``` +User 0: + [...] + runtime permissions: + android.permission.READ_EXTERNAL_STORAGE: granted=true, flags=[ SYSTEM_FIXED|GRANTED_BY_DEFAULT ] +``` + +#### Background access + +Whether the app is currently visible to the user is reflected in the `ActivityManager`'s proc state. +There is a lot of granularity to this, but runtime permissions are using the [app-ops services' +](../app/AppOps.md) definition of foreground and background. + +Most runtime permissions are not affected by foreground/background-ness. Microphone and Camera are +foreground-only while Location is usually foreground-only, but background access can be added by +granting the `ACCESS_BACKGROUND_LOCATION` modifier runtime permission. + +##### Microphone and Camera + +Currently these only allow access while in the app is in foreground. There is a manual whitelist +for e.g. the voice interaction service. + +This is currently (Mar 2020) reworked and will behave like [location](#location) soon. + +##### Location + +As described [above](#runtime-permissions-and-app-ops) the app-op mode for granted permissions is +`MODE_ALLOWED` to allow access or `MODE_IGNORED` to suppress access. + +The important case is the case where the permission is granted and the app-op is `MODE_IGNORED`. In +the case of location this state causes the `LocationManagerService` to stop delivering locations to +the app. This is not a breaking behavior as the same scenarios happens if e.g. no satellites +could be found. + +This behavior is used to implement the foregound/background behavior for location. If the app is +in the foreground the app-op mode is `MODE_ALLOWED` and works normally. If the app goes into +background the app-op mode changes to `MODE_IGNORED`. This means that locations are delivered while +the app is in foreground and while the app is background, the app won't get any locations. + +The automatic switching between `MODE_ALLOWED` and `MODE_IGNORED` is done inside of + [`AppOpsManager`](../app/AppOps.md#foreground). + +Background access can be enabled by also granting the `ACCESS_BACKGROUND_LOCATION` to the app. In +this case the app-op mode will always be `MODE_ALLOWED`. + +#### UI + +##### Granting + +An app following the best practices does not ask for any runtime permissions until absolutely +needed. Once needed the request should be made in context. I.e. the user should understand from the +current state of the app and the user's action why the request is made. E.g. if the user presses +a "show me the next ATM"-button the user is most likely expecting a request for the location +permission. + +This is central premise to the runtime permission UI. It is the app's responsibility to avoid +showing permission requests dialogs to the user which might get denied. These dialogs are not +meant to be user-choices, they are meant to be user-confirmations. + +Hence any denied permission dialog is probably due to the app asking for permissions the user +does not expect. If too many permission requests get denied the app is apparently trying to get +more than the user wants to give to the app. In this case the permission gets permanently denied +and all future requests will be denied automatically without showing a UI. + +`Context.requestPermission` calls for more than one permission are allowed and might result in +multiple dialogs in sequence. This might make sense for e.g. getting microphone and camera +permission when starting a video call. + +Each time the the user makes a choice (either to grant or the deny) a permission request the +permission is marked as `USER_SET`. If a permission gets permanently denied the permission is marked +as `USER_FIXED`. + +This can be found in `dumpsys package my.package.name` +``` +User 0: + [...] + runtime permissions: + android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ USER_SET|USER_FIXED ] + android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ USER_SET ] +``` + +##### Settings + +By far most interactions with the permission system are via the [permission grant flow](#granting). +The main purpose of the permission settings is to show the user the previous choices and allow +the user to revisit previous choices. In reality few users do that. + +##### Grouping + +There are too many runtime permissions for the user to individually manage. Hence the UI bundles the +permissions into groups. **Apps should never assume the grouping**. The grouping might change +with SDK updates, but also at any other time. Certain form factors or locales might use other +permission models and sometimes some of the permissions of a group cannot be granted for various +reasons. The grouping is defined inside the permission controller app. + +If two permissions belong to a group and the first permission is already granted the second one +will be granted on request of the app without user interaction. For that reason a permission +group with at least one individual permission granted will show up as granted in the UI. + +##### Alternate permission management + +It is not allowed to build alternate permission management UIs. While restricting innovation is not +a good choice this is a required one to enforce a consistent, predictable, but flexible permission +model for users and app developers. + +Further some data needed for permission management (e.g. the grouping) is not available outside +the permission controller app. + +Hence all permission management UI needs to be integrated with AOSP. + +#### Pre granting + +Runtime permissions protect user private data. It is a violation of user trust to give the data +to an app without explicit user consent (i.e. the user [granting](#granting) the permission +). Still the user expects certain functionality (e.g. receiving a phone call) to work out of the +box. + +Hence the `DefaultPermissionGrantPolicy` and roles allow to grant permission without the user +. The default permission grant policy grants permissions for three categories of apps +- Apps running in well defined [uids](../os/Users.md#int-uid) as they are considered as part of + the platform +- Apps that are in certain predefined categories, e.g. the browser and the SMS app. This is + meant for the most basic phone functionality, not for all pre-installed apps. +- Apps that are explicitly mentioned as a pre-grant-exceptions. This is meant to be used for setup + and other highly critical use cases, not to improve the user experience. The exceptions are listed + in xml files in `etc/` and follow the following syntax +```xml +<exceptions> + <exception package="my.package.name"> + <permission name="android.permission.ACCESS_FINE_LOCATION" fixed="false"/> + </exception> +</exceptions> +``` + +Pre-granted runtime permissions can still be revoked by the user in [settings](#settings) unless +they are granted as `SYSTEM_FIXED`. + +Whether a permission was granted by the default can be checked in the permission flags of +`dumpsys package my.package.name` + +``` +User 0: + [...] + runtime permissions: + android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ GRANTED_BY_DEFAULT ] +``` + +### Permission restricted components + +As [publicly documented](https://developer.android.com/guide/topics/permissions/overview#permission_enforcement) +it is possible to restrict starting an activity/binding to a service by using permission. It is +a common pattern to + +- define a permission in the platform as `signature` +- protect a service in an app by this permission using the `android:permission` attribute of the + `<service>` tag + +Then it is guaranteed that only the system can bind to such service. This is used for services +that provide extensions to platform functionality, such as auto-fill services, print services, and +accessibility services. + +This does not work for app-op or runtime permissions as the way to check these permissions is +more complex than install time permissions. + +## Permissions for system apps + +System apps need to integrate deeper with the system than regular apps. Hence they need to be +able to call APIs not available to other apps. This is implemented by granting permissions to +these system apps and then enforcing the permissions in the API similar to other [install time +permissions](#checking-a-permission). + +System apps are not different from regular apps, but the protection levels (e.g. +[privileged](#privileged-permissions), [preinstalled](#preinstalled-permissions)) mentioned in this +section are more commonly used by system apps. + +### Multiple permission levels + +It is possible to assign multiple protection levels to a permission. Very common combinations are +for example adding `signature` to all permissions to make sure the platform signed apps can be +granted the permission, e.g. `privileged|signature`. + +The permission will be granted if the app qualifies for _any_ of the permission levels. + +### App-op permissions + +> See [App-ops](../app/AppOps.md). + +App-op permissions are user-switchable permissions that are not runtime permissions. This should +be used for permissions that are really only meant to be ever granted to a very small amount of +apps. Traditionally granting these permissions is intentionally very heavy weight so that the +user really needs to understand the use case. For example one use case is the +`INTERACT_ACROSS_PROFILES` permission that allows apps of different +[user profiles](../os/Users.md#user-profile) to interact. Of course this is breaking a very basic +security container and hence should only every be granted with a lot of care. + +**Warning:** Most app-op permissions follow this logic, but most of them also have exceptions +and special behavior. Hence this section is a guideline, not a rule. + +#### Defining an app-op permission + +Only the platform can reasonably define an app-op permission. The permission is defined in the +platforms manifest using the `appop` protection level + +```xml +<manifest package="android"> + <permission android:name="android.permission.MY_APPOP_PERMISSION" + android:protectionLevel="appop|signature" /> +</manifest> +``` + +Almost always the protection level is app-op | something else, like +[signature](#signature-permissions) (in the case above) or [privileged](#privileged-permissions). + +#### Checking a app-op permission + +The `PermissionChecker` utility can check app-op permissions with the [same syntax as runtime +permissions](#checking-a-runtime-permission). + +The permission checker internally follows this flow + +```kotlin +class PermissionChecker { + fun checkCallingPermission(context: Context, permission: String) { + if (isAppOpPermission(permission)) { + val appopOfPermission = AppOpsManager.permissionToOp(permission) + if (appopOfPermission == null) { + // not platform defined + return PERMISSION_DENIED + } + + val appopMode = appOpsManager.noteOp(appopOfPermission) + when (appopMode) { + AppOpsManager.MODE_ALLOWED -> return PERMISSION_GRANTED + AppOpsManager.MODE_IGNORED -> return PERMISSION_DENIED + AppOpsManager.MODE_DEFAULT -> { + if (context.checkPermission(uid, permission) == GRANTED) { + return PERMISSION_GRANTED + } else { + return PERMISSION_DENIED + } + } + } + } else { + return PERMISSION_DENIED + } + } +} +``` + +#### Granting a app-op permission + +The permission's grant state is only considered if the app-op's mode is `MODE_DEFAULT`. This +allows to have default grants while still being overridden by the app-op. + +The permission is then granted by setting the app-op mode. This is usually done via dedicated APIs +for each use cases. Similarly whether and how an app can request the permission is different for +each app-op permission. + +When implementing a new app-op permission, make sure to set the app-op mode using `AppOpsManager +.setUidMode` to make sure the permission is granted on the uid as this is the security domain. + +During development app-ops can be grated to app via the `appops set` shell command. E.g. + +``` +adb shell appops set 10187 INTERACT_ACROSS_PROFILES allow +``` + +sets the `INTERACT_ACROSS_PROFILES` app-op for uid 10187 to allow thereby granting apps in this +uid the ability to interact across profiles. + +##### UI + +Most UIs for app-op permissions are in the "Special app access" section of the settings app. + +In most cases the permission should only be granted with the user's explicit agreement, usually by +allowing the app to directly open the "Special app access" page for this permission and app. + +To repeat: this is a guideline for app-op permissions and there are many exceptions. + +### Signature permissions + +Only apps signed with the defining app's certificate will be granted the permission. This is +used to restrict APIs to apps of the same developer. + +This is frequently used to restrict permissions defined by the platform to apps also signed with +the platform's certificate. As this is a very tight restriction this is recommended for +permissions that are only used by apps built out of AOSP which are signed with the platform +certificate. + +Please note that OEMs sign their platform them self. I.e. OEMs can implement new apps using these +permissions. It is unlikely that 3rd party apps will be able to use APIs protected by signature +permissions as they are usually not signed with the platform certificate. + +Such permissions are defined and checked like an install time permission. + +### Preinstalled permissions + +This means that the app has to be pre-installed. There is no restriction what apps are pre-installed +on a particular device install there. Hence it can be really any app including 3rd party apps. + +Hence this permission level is discouraged unless there are +[further restrictions](#restricted-by-tests). + +Such permissions are defined and checked like an install time permission. + +### Privileged permissions + +This means that the app has to be pre-installed and in the `system/priv` directory in the +filesystem. There is no restriction what apps are in this directory on a particular device +install there. Hence it can be really any app including 3rd party apps. + +An app is only ever granted privileged permissions requested by the pre-installed apk. I.e. +privileged permissions added in updates will never be granted. + +Hence this permission level is discouraged unless there are +[further restrictions](#restricted-by-tests). + +Such permissions are defined and checked like an install time permission. + +#### Restricted by tests + +As all apps that might get preinstalled or privilidged permissions need to be pre-installed and new +images need to pass compliance tests it is possible to use a test to whitelist the apps that can +request the permission. + +Example of such a test: +```kotlin +/* Add new whitelisted packages to this list */ +private val whitelistedPkgs = listOf("my.whitelisted.package") + +@Test +fun onlySomeAppsAreAllowedToHavePermissionGranted() { + assertThat(whitelistedPkgs).containsAllIn( + context.packageManager.getInstalledPackages(MATCH_ALL) + .filter { pkg -> + context.checkPermission(android.Manifest.permission.MY_PRIVILEGED_PERMISSION, -1, + pkg.applicationInfo.uid) == PERMISSION_GRANTED + /* The permission is defined by the system and hence granted to it */ + }.filter { pkg -> pkg.applicationInfo.uid != SYSTEM_UID } + .map { it.packageName } + ) +} +``` + +#### Whitelist + +As mentioned above it is not suggested, but still common practice to install 3rd party apps as +privilidged. To verify and restrict which privilidged permissions those apps get granted all +privilidged permissions need to be explicitly whitelisted in a file `/etc`. + +```xml +<permissions> + <privapp-permissions package="my.privileged.package"> + <!-- allow the app to request a permission --> + <permission name="android.permission.MY_PRIVILEGED_PERMISSION"/> + + <!-- Even though the app requests the permission, do not grant it --> + <deny-permission name="android.permission.MY_OTHER_PRIVILEGED_PERMISSION"/> + </privapp-permissions> +</permissions> +``` + +If the pre-installed apk of app requests a privileged permission that is not mentioned in any +whitelist or that is not denied the system will refuse to boot. As mentioned above privileged +permissions added in updates to the pre-installed app will never be granted. + +### Limited permissions + +E.g. installer, wellbeing, documenter, etc... This allows the system to restrict the permission to a +well defined app or set of apps. It is possible to add new types in `PackageManagerService`. + +Which apps qualify for such a permission level is flexible and custom for each such level. Usually +they refer to a single or small set of apps, usually - but not always - apps defined in AOSP. + +These permissions are defined and checked like an install time permission. + +### Development permissions + +> Not recommended + +By adding the `development` protection level to any permissions the permission can be granted via +the `pm grant` shell command. This appears to be useful for development and testing, but it is very +highly discouraged. Any user can grant them permanently via adb, hence adding this tag removes +all guarantees the permission might otherwise provide. + +### Other protection levels + +There are other levels (such as `runtime`) but they are for special purposes on should not be +used by platform developers. diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java index 1eb76648f7b2..dd2ea81d747b 100644 --- a/core/java/android/provider/BlockedNumberContract.java +++ b/core/java/android/provider/BlockedNumberContract.java @@ -16,7 +16,6 @@ package android.provider; import android.annotation.IntDef; -import android.annotation.SystemApi; import android.annotation.WorkerThread; import android.content.Context; import android.net.Uri; @@ -240,7 +239,6 @@ public class BlockedNumberContract { * blocked. * @hide */ - @SystemApi public static final int STATUS_NOT_BLOCKED = 0; /** @@ -248,7 +246,6 @@ public class BlockedNumberContract { * because it is in the list of blocked numbers maintained by the provider. * @hide */ - @SystemApi public static final int STATUS_BLOCKED_IN_LIST = 1; /** @@ -256,7 +253,6 @@ public class BlockedNumberContract { * because it is from a restricted number. * @hide */ - @SystemApi public static final int STATUS_BLOCKED_RESTRICTED = 2; /** @@ -264,7 +260,6 @@ public class BlockedNumberContract { * because it is from an unknown number. * @hide */ - @SystemApi public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3; /** @@ -272,7 +267,6 @@ public class BlockedNumberContract { * because it is from a pay phone. * @hide */ - @SystemApi public static final int STATUS_BLOCKED_PAYPHONE = 4; /** @@ -280,14 +274,12 @@ public class BlockedNumberContract { * because it is from a number not in the users contacts. * @hide */ - @SystemApi public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5; /** * Integer reason indicating whether a call was blocked, and if so why. * @hide */ - @SystemApi public static final String RES_BLOCK_STATUS = "block_status"; /** @hide */ @@ -298,31 +290,6 @@ public class BlockedNumberContract { "can_current_user_block_numbers"; /** @hide */ - @SystemApi - public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact"; - - /** @hide */ - public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression"; - - /** @hide */ - @SystemApi - public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number"; - - /** @hide */ - public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS = - "get_block_suppression_status"; - - /** @hide */ - public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION = - "should_show_emergency_call_notification"; - - /** @hide */ - public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting"; - - /** @hide */ - public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting"; - - /** @hide */ public static final String RES_CAN_BLOCK_NUMBERS = "can_block"; /** @hide */ @@ -439,11 +406,26 @@ public class BlockedNumberContract { public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED = "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED"; + public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact"; + + public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression"; + + public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number"; + + public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS = + "get_block_suppression_status"; + + public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION = + "should_show_emergency_call_notification"; + public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed"; public static final String RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP = "blocking_suppressed_until_timestamp"; + public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting"; + public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting"; + /* Preference key of block numbers not in contacts setting. */ public static final String ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED = "block_numbers_not_in_contacts_setting"; diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index aa511cc46de9..fb81d675939c 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -399,6 +399,13 @@ public final class DeviceConfig { public static final String NAMESPACE_CONNECTIVITY_THERMAL_POWER_MANAGER = "connectivity_thermal_power_manager"; + /** + * Namespace for configuration related features. + * + * @hide + */ + public static final String NAMESPACE_CONFIGURATION = "configuration"; + private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners = diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index a80153d691f0..327bca268a7b 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -1081,7 +1081,7 @@ public abstract class DocumentsProvider extends ContentProvider { // signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for // MANAGE_DOCUMENTS or associated URI permission here instead final Uri rootUri = extras.getParcelable(DocumentsContract.EXTRA_URI); - enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingFeatureId(), + enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingAttributionTag(), null); final String rootId = DocumentsContract.getRootId(rootUri); @@ -1103,8 +1103,8 @@ public abstract class DocumentsProvider extends ContentProvider { enforceTree(documentUri); if (METHOD_IS_CHILD_DOCUMENT.equals(method)) { - enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), - null); + enforceReadPermissionInner(documentUri, getCallingPackage(), + getCallingAttributionTag(), null); final Uri childUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); final String childAuthority = childUri.getAuthority(); @@ -1116,8 +1116,8 @@ public abstract class DocumentsProvider extends ContentProvider { && isChildDocument(documentId, childId)); } else if (METHOD_CREATE_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), - null); + enforceWritePermissionInner(documentUri, getCallingPackage(), + getCallingAttributionTag(), null); final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE); final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); @@ -1131,8 +1131,8 @@ public abstract class DocumentsProvider extends ContentProvider { out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); } else if (METHOD_CREATE_WEB_LINK_INTENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), - null); + enforceWritePermissionInner(documentUri, getCallingPackage(), + getCallingAttributionTag(), null); final Bundle options = extras.getBundle(DocumentsContract.EXTRA_OPTIONS); final IntentSender intentSender = createWebLinkIntent(documentId, options); @@ -1140,8 +1140,8 @@ public abstract class DocumentsProvider extends ContentProvider { out.putParcelable(DocumentsContract.EXTRA_RESULT, intentSender); } else if (METHOD_RENAME_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), - null); + enforceWritePermissionInner(documentUri, getCallingPackage(), + getCallingAttributionTag(), null); final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); final String newDocumentId = renameDocument(documentId, displayName); @@ -1165,8 +1165,8 @@ public abstract class DocumentsProvider extends ContentProvider { } } else if (METHOD_DELETE_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), - null); + enforceWritePermissionInner(documentUri, getCallingPackage(), + getCallingAttributionTag(), null); deleteDocument(documentId); // Document no longer exists, clean up any grants. @@ -1176,9 +1176,9 @@ public abstract class DocumentsProvider extends ContentProvider { final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); final String targetId = DocumentsContract.getDocumentId(targetUri); - enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), - null); - enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(), + enforceReadPermissionInner(documentUri, getCallingPackage(), + getCallingAttributionTag(), null); + enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingAttributionTag(), null); final String newDocumentId = copyDocument(documentId, targetId); @@ -1202,11 +1202,11 @@ public abstract class DocumentsProvider extends ContentProvider { final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); final String targetId = DocumentsContract.getDocumentId(targetUri); - enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), - null); - enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(), - null); - enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(), + enforceWritePermissionInner(documentUri, getCallingPackage(), + getCallingAttributionTag(), null); + enforceReadPermissionInner(parentSourceUri, getCallingPackage(), + getCallingAttributionTag(), null); + enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingAttributionTag(), null); final String newDocumentId = moveDocument(documentId, parentSourceId, targetId); @@ -1228,10 +1228,10 @@ public abstract class DocumentsProvider extends ContentProvider { final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI); final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri); - enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(), - null); - enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), - null); + enforceReadPermissionInner(parentSourceUri, getCallingPackage(), + getCallingAttributionTag(), null); + enforceWritePermissionInner(documentUri, getCallingPackage(), + getCallingAttributionTag(), null); removeDocument(documentId, parentSourceId); // It's responsibility of the provider to revoke any grants, as the document may be @@ -1240,8 +1240,8 @@ public abstract class DocumentsProvider extends ContentProvider { final boolean isTreeUri = isTreeUri(documentUri); if (isTreeUri) { - enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), - null); + enforceReadPermissionInner(documentUri, getCallingPackage(), + getCallingAttributionTag(), null); } else { getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 641de4a71142..ce9b4496f275 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -98,7 +98,8 @@ import java.util.Set; * The Settings provider contains global system-level device preferences. */ public final class Settings { - private static final boolean DEFAULT_OVERRIDEABLE_BY_RESTORE = false; + /** @hide */ + public static final boolean DEFAULT_OVERRIDEABLE_BY_RESTORE = false; // Intent actions for Settings @@ -709,10 +710,7 @@ public final class Settings { * If not specified, the default behavior is * {@link android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_WEAK}. * <p> - * Output: Returns {@link android.app.Activity#RESULT_CANCELED} if the user already has an - * authenticator that meets the requirements, or if the device cannot fulfill the request - * (e.g. does not have biometric hardware). Returns {@link android.app.Activity#RESULT_OK} - * otherwise. Note that callers should still check + * Output: Nothing. Note that callers should still check * {@link android.hardware.biometrics.BiometricManager#canAuthenticate(int)} * afterwards to ensure that the user actually completed enrollment. */ @@ -2626,7 +2624,7 @@ public final class Settings { arg.putBoolean(CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY, true); } IContentProvider cp = mProviderHolder.getProvider(cr); - cp.call(cr.getPackageName(), cr.getFeatureId(), + cp.call(cr.getPackageName(), cr.getAttributionTag(), mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg); } catch (RemoteException e) { Log.w(TAG, "Can't set key " + name + " in " + mUri, e); @@ -2646,7 +2644,7 @@ public final class Settings { args.putString(CALL_METHOD_PREFIX_KEY, prefix); args.putSerializable(CALL_METHOD_FLAGS_KEY, keyValues); IContentProvider cp = mProviderHolder.getProvider(cr); - Bundle bundle = cp.call(cr.getPackageName(), cr.getFeatureId(), + Bundle bundle = cp.call(cr.getPackageName(), cr.getAttributionTag(), mProviderHolder.mUri.getAuthority(), mCallSetAllCommand, null, args); return bundle.getBoolean(KEY_CONFIG_SET_RETURN); @@ -2721,14 +2719,14 @@ public final class Settings { if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) { final long token = Binder.clearCallingIdentity(); try { - b = cp.call(cr.getPackageName(), cr.getFeatureId(), + b = cp.call(cr.getPackageName(), cr.getAttributionTag(), mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args); } finally { Binder.restoreCallingIdentity(token); } } else { - b = cp.call(cr.getPackageName(), cr.getFeatureId(), + b = cp.call(cr.getPackageName(), cr.getAttributionTag(), mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args); } if (b != null) { @@ -2798,13 +2796,13 @@ public final class Settings { if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) { final long token = Binder.clearCallingIdentity(); try { - c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri, + c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri, SELECT_VALUE_PROJECTION, queryArgs, null); } finally { Binder.restoreCallingIdentity(token); } } else { - c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri, + c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri, SELECT_VALUE_PROJECTION, queryArgs, null); } if (c == null) { @@ -2897,7 +2895,7 @@ public final class Settings { } // Fetch all flags for the namespace at once for caching purposes - Bundle b = cp.call(cr.getPackageName(), cr.getFeatureId(), + Bundle b = cp.call(cr.getPackageName(), cr.getAttributionTag(), mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args); if (b == null) { // Invalid response, return an empty map @@ -5542,7 +5540,7 @@ public final class Settings { } arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode); IContentProvider cp = sProviderHolder.getProvider(resolver); - cp.call(resolver.getPackageName(), resolver.getFeatureId(), + cp.call(resolver.getPackageName(), resolver.getAttributionTag(), sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SECURE, null, arg); } catch (RemoteException e) { Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e); @@ -6092,10 +6090,7 @@ public final class Settings { * device is removed from this mode. * <p> * Type: int (0 for false, 1 for true) - * - * @hide */ - @SystemApi public static final String SECURE_FRP_MODE = "secure_frp_mode"; /** @@ -13388,7 +13383,7 @@ public final class Settings { } arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode); IContentProvider cp = sProviderHolder.getProvider(resolver); - cp.call(resolver.getPackageName(), resolver.getFeatureId(), + cp.call(resolver.getPackageName(), resolver.getAttributionTag(), sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_GLOBAL, null, arg); } catch (RemoteException e) { Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e); @@ -14363,7 +14358,7 @@ public final class Settings { arg.putString(Settings.CALL_METHOD_PREFIX_KEY, createPrefix(namespace)); } IContentProvider cp = sProviderHolder.getProvider(resolver); - cp.call(resolver.getPackageName(), resolver.getFeatureId(), + cp.call(resolver.getPackageName(), resolver.getAttributionTag(), sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_CONFIG, null, arg); } catch (RemoteException e) { Log.w(TAG, "Can't reset to defaults for " + DeviceConfig.CONTENT_URI, e); @@ -14392,7 +14387,7 @@ public final class Settings { arg.putInt(CALL_METHOD_USER_KEY, userHandle); arg.putParcelable(CALL_METHOD_MONITOR_CALLBACK_KEY, callback); IContentProvider cp = sProviderHolder.getProvider(resolver); - cp.call(resolver.getPackageName(), resolver.getFeatureId(), + cp.call(resolver.getPackageName(), resolver.getAttributionTag(), sProviderHolder.mUri.getAuthority(), CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG, null, arg); } catch (RemoteException e) { diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 629dc8b53265..e7b360da47b8 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -3551,7 +3551,6 @@ public final class Telephony { * can manage DPC-owned APNs. * @hide */ - @SystemApi public static final @NonNull Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc"); /** @@ -3735,7 +3734,6 @@ public final class Telephony { * @deprecated this column is no longer supported, use {@link #NETWORK_TYPE_BITMASK} instead */ @Deprecated - @SystemApi public static final String BEARER_BITMASK = "bearer_bitmask"; /** @@ -3785,7 +3783,6 @@ public final class Telephony { * <p>Type: INTEGER</p> *@hide */ - @SystemApi public static final String PROFILE_ID = "profile_id"; /** @@ -3866,7 +3863,6 @@ public final class Telephony { * Integer value denoting an invalid APN id * @hide */ - @SystemApi public static final int INVALID_APN_ID = -1; /** @@ -3986,7 +3982,6 @@ public final class Telephony { * * @hide */ - @SystemApi public static final String SKIP_464XLAT = "skip_464xlat"; /** @@ -3995,7 +3990,6 @@ public final class Telephony { * * @hide */ - @SystemApi public static final int SKIP_464XLAT_DEFAULT = -1; /** @@ -4004,7 +3998,6 @@ public final class Telephony { * * @hide */ - @SystemApi public static final int SKIP_464XLAT_DISABLE = 0; /** @@ -4013,7 +4006,6 @@ public final class Telephony { * * @hide */ - @SystemApi public static final int SKIP_464XLAT_ENABLE = 1; @@ -4392,6 +4384,7 @@ public final class Telephony { * Indicates that whether the message has been broadcasted to the application. * <P>Type: BOOLEAN</P> */ + // TODO: deprecate this in S. public static final String MESSAGE_BROADCASTED = "message_broadcasted"; /** diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 886b433d4ade..08aa534be152 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -232,22 +232,6 @@ public final class Dataset implements Parcelable { * Creates a new builder. * * @param presentation The presentation used to visualize this dataset. - * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset - * as inline suggestions. If the dataset supports inline suggestions, - * this should not be null. - */ - public Builder(@NonNull RemoteViews presentation, - @NonNull InlinePresentation inlinePresentation) { - Preconditions.checkNotNull(presentation, "presentation must be non-null"); - Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null"); - mPresentation = presentation; - mInlinePresentation = inlinePresentation; - } - - /** - * Creates a new builder. - * - * @param presentation The presentation used to visualize this dataset. */ public Builder(@NonNull RemoteViews presentation) { Preconditions.checkNotNull(presentation, "presentation must be non-null"); @@ -282,6 +266,22 @@ public final class Dataset implements Parcelable { } /** + * Sets the {@link InlinePresentation} used to visualize this dataset as inline suggestions. + * If the dataset supports inline suggestions this should not be null. + * + * @throws IllegalStateException if {@link #build()} was already called. + * + * @return this builder. + */ + public @NonNull Builder setInlinePresentation( + @NonNull InlinePresentation inlinePresentation) { + throwIfDestroyed(); + Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null"); + mInlinePresentation = inlinePresentation; + return this; + } + + /** * Triggers a custom UI before before autofilling the screen with the contents of this * dataset. * @@ -600,7 +600,7 @@ public final class Dataset implements Parcelable { */ @SystemApi @TestApi - public @NonNull Builder setInlinePresentation(@NonNull AutofillId id, + public @NonNull Builder setFieldInlinePresentation(@NonNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull InlinePresentation inlinePresentation) { throwIfDestroyed(); @@ -700,7 +700,7 @@ public final class Dataset implements Parcelable { final Builder builder = presentation != null ? inlinePresentation == null ? new Builder(presentation) - : new Builder(presentation, inlinePresentation) + : new Builder(presentation).setInlinePresentation(inlinePresentation) : inlinePresentation == null ? new Builder() : new Builder(inlinePresentation); diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 72e9ad047ed7..8f858d547b1f 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -77,6 +77,15 @@ public final class FillRequest implements Parcelable { */ public static final @RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2; + /** + * Indicates the request came from a password field. + * + * (TODO: b/141703197) Temporary fix for augmented autofill showing passwords. + * + * @hide + */ + public static final @RequestFlags int FLAG_PASSWORD_INPUT_TYPE = 0x4; + /** @hide */ public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE; @@ -149,7 +158,8 @@ public final class FillRequest implements Parcelable { /** @hide */ @IntDef(flag = true, prefix = "FLAG_", value = { FLAG_MANUAL_REQUEST, - FLAG_COMPATIBILITY_MODE_REQUEST + FLAG_COMPATIBILITY_MODE_REQUEST, + FLAG_PASSWORD_INPUT_TYPE }) @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member @@ -169,6 +179,8 @@ public final class FillRequest implements Parcelable { return "FLAG_MANUAL_REQUEST"; case FLAG_COMPATIBILITY_MODE_REQUEST: return "FLAG_COMPATIBILITY_MODE_REQUEST"; + case FLAG_PASSWORD_INPUT_TYPE: + return "FLAG_PASSWORD_INPUT_TYPE"; default: return Integer.toHexString(value); } } @@ -223,7 +235,8 @@ public final class FillRequest implements Parcelable { Preconditions.checkFlagsArgument( mFlags, FLAG_MANUAL_REQUEST - | FLAG_COMPATIBILITY_MODE_REQUEST); + | FLAG_COMPATIBILITY_MODE_REQUEST + | FLAG_PASSWORD_INPUT_TYPE); this.mInlineSuggestionsRequest = inlineSuggestionsRequest; onConstructed(); @@ -352,7 +365,8 @@ public final class FillRequest implements Parcelable { Preconditions.checkFlagsArgument( mFlags, FLAG_MANUAL_REQUEST - | FLAG_COMPATIBILITY_MODE_REQUEST); + | FLAG_COMPATIBILITY_MODE_REQUEST + | FLAG_PASSWORD_INPUT_TYPE); this.mInlineSuggestionsRequest = inlineSuggestionsRequest; onConstructed(); @@ -373,10 +387,10 @@ public final class FillRequest implements Parcelable { }; @DataClass.Generated( - time = 1575928271155L, + time = 1583196707026L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java", - inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") + inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl index 101165176293..1bcc76bfca44 100644 --- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl +++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl @@ -25,7 +25,8 @@ import android.view.SurfaceControlViewHost; * @hide */ oneway interface IInlineSuggestionUiCallback { - void onAutofill(); + void onClick(); + void onLongClick(); void onContent(in SurfaceControlViewHost.SurfacePackage surface); void onError(); void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId); diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java index ee15283715ff..b6cc62dc213e 100644 --- a/core/java/android/service/autofill/InlineSuggestionRenderService.java +++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java @@ -97,12 +97,21 @@ public abstract class InlineSuggestionRenderService extends Service { host.addView(suggestionRoot, lp); suggestionRoot.setOnClickListener((v) -> { try { - callback.onAutofill(); + callback.onClick(); } catch (RemoteException e) { - Log.w(TAG, "RemoteException calling onAutofill()"); + Log.w(TAG, "RemoteException calling onClick()"); } }); + suggestionRoot.setOnLongClickListener((v) -> { + try { + callback.onLongClick(); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling onLongClick()"); + } + return true; + }); + sendResult(callback, host.getSurfacePackage()); } finally { updateDisplay(Display.DEFAULT_DISPLAY); diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index ed27dd568823..5b08ae20f071 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -488,7 +488,8 @@ public abstract class AugmentedAutofillService extends Service { ids.add(pair.first); values.add(pair.second); } - mClient.autofill(mSessionId, ids, values); + final boolean hideHighlight = size == 1 && ids.get(0).equals(mFocusedId); + mClient.autofill(mSessionId, ids, values, hideHighlight); } public void setFillWindow(@NonNull FillWindow fillWindow) { diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java index 0b9a8aff26e8..d4db79eda762 100644 --- a/core/java/android/service/dataloader/DataLoaderService.java +++ b/core/java/android/service/dataloader/DataLoaderService.java @@ -18,6 +18,7 @@ package android.service.dataloader; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; @@ -206,6 +207,7 @@ public abstract class DataLoaderService extends Service { * @throws IOException if trouble opening the file for writing, such as lack of disk space * or unavailable media. */ + @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void writeData(@NonNull String name, long offsetBytes, long lengthBytes, @NonNull ParcelFileDescriptor incomingFd) throws IOException { try { diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 002d4b8d195d..e70311fb9429 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -609,9 +609,7 @@ public class DreamService extends Service implements Window.Callback { * * @hide * - * TODO: Remove @UnsupportedAppUsage. */ - @UnsupportedAppUsage public void setWindowless(boolean windowless) { mWindowless = windowless; } diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 8e6f77b2fd0c..4a0dd870f797 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1314,7 +1314,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(mContext); int res = mSystemService.startVoiceActivity(mToken, intent, - intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId()); + intent.resolveType(mContext.getContentResolver()), + mContext.getAttributionTag()); Instrumentation.checkStartActivityResult(res, intent); } catch (RemoteException e) { } @@ -1342,7 +1343,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(mContext); int res = mSystemService.startAssistantActivity(mToken, intent, - intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId()); + intent.resolveType(mContext.getContentResolver()), + mContext.getAttributionTag()); Instrumentation.checkStartActivityResult(res, intent); } catch (RemoteException e) { } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index bf3c08870c63..f531e12b0748 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -124,8 +124,6 @@ public abstract class WallpaperService extends Service { private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050; private static final int MSG_SCALE = 10100; - private static final float MAX_SCALE = 1.15f; - private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; private final ArrayList<Engine> mActiveEngines @@ -351,7 +349,7 @@ public abstract class WallpaperService extends Service { @Override public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, - boolean sync) { + float zoom, boolean sync) { synchronized (mLock) { if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); mPendingXOffset = x; @@ -366,6 +364,8 @@ public abstract class WallpaperService extends Service { Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); mCaller.sendMessage(msg); } + Message msg = mCaller.obtainMessageI(MSG_SCALE, Float.floatToIntBits(zoom)); + mCaller.sendMessage(msg); } } @@ -462,6 +462,18 @@ public abstract class WallpaperService extends Service { } /** + * This will be called when the wallpaper is first started. If true is returned, the system + * will zoom in the wallpaper by default and zoom it out as the user interacts, + * to create depth. Otherwise, zoom will have to be handled manually + * in {@link #onZoomChanged(float)}. + * + * @hide + */ + public boolean shouldZoomOutWallpaper() { + return false; + } + + /** * Control whether this wallpaper will receive raw touch events * from the window manager as the user interacts with the window * that is currently displaying the wallpaper. By default they @@ -870,6 +882,7 @@ public abstract class WallpaperService extends Service { Log.w(TAG, "Failed to add window while updating wallpaper surface."); return; } + mSession.setShouldZoomOutWallpaper(mWindow, shouldZoomOutWallpaper()); mCreated = true; mInputEventReceiver = new WallpaperInputEventReceiver( @@ -964,7 +977,6 @@ public abstract class WallpaperService extends Service { c.surfaceCreated(mSurfaceHolder); } } - onZoomChanged(0f); } redrawNeeded |= creating || (relayoutResult @@ -1125,7 +1137,6 @@ public abstract class WallpaperService extends Service { mIsInAmbientMode = inAmbientMode; if (mCreated) { onAmbientModeChanged(inAmbientMode, animationDuration); - setZoom(0); } } } diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java index e93ba16fa594..92f3538a48de 100644 --- a/core/java/android/speech/SpeechRecognizer.java +++ b/core/java/android/speech/SpeechRecognizer.java @@ -342,7 +342,7 @@ public class SpeechRecognizer { } try { mService.startListening(recognizerIntent, mListener, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); if (DBG) Log.d(TAG, "service start listening command succeded"); } catch (final RemoteException e) { Log.e(TAG, "startListening() failed", e); @@ -357,7 +357,7 @@ public class SpeechRecognizer { } try { mService.stopListening(mListener, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); if (DBG) Log.d(TAG, "service stop listening command succeded"); } catch (final RemoteException e) { Log.e(TAG, "stopListening() failed", e); @@ -371,7 +371,7 @@ public class SpeechRecognizer { return; } try { - mService.cancel(mListener, mContext.getOpPackageName(), mContext.getFeatureId()); + mService.cancel(mListener, mContext.getOpPackageName(), mContext.getAttributionTag()); if (DBG) Log.d(TAG, "service cancel command succeded"); } catch (final RemoteException e) { Log.e(TAG, "cancel() failed", e); @@ -400,7 +400,8 @@ public class SpeechRecognizer { public void destroy() { if (mService != null) { try { - mService.cancel(mListener, mContext.getOpPackageName(), mContext.getFeatureId()); + mService.cancel(mListener, mContext.getOpPackageName(), + mContext.getAttributionTag()); } catch (final RemoteException e) { // Not important } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 7238b1244eb3..ab9df56560b3 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -116,7 +116,7 @@ public class TelephonyRegistryManager { mSubscriptionChangedListenerMap.put(listener, callback); try { sRegistry.addOnSubscriptionsChangedListener(mContext.getOpPackageName(), - mContext.getFeatureId(), callback); + mContext.getAttributionTag(), callback); } catch (RemoteException ex) { // system server crash } @@ -175,7 +175,7 @@ public class TelephonyRegistryManager { mOpportunisticSubscriptionChangedListenerMap.put(listener, callback); try { sRegistry.addOnOpportunisticSubscriptionsChangedListener(mContext.getOpPackageName(), - mContext.getFeatureId(), callback); + mContext.getAttributionTag(), callback); } catch (RemoteException ex) { // system server crash } diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java index 2aca36aeff0f..82c7ea705aa6 100644 --- a/core/java/android/text/util/Linkify.java +++ b/core/java/android/text/util/Linkify.java @@ -19,6 +19,7 @@ package android.text.util; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.telephony.PhoneNumberUtils; @@ -663,9 +664,11 @@ public class Linkify { private static void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s, @Nullable Context context) { PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); + final Context ctx = (context != null) ? context : ActivityThread.currentApplication(); + final String regionCode = (ctx != null) ? ctx.getSystemService(TelephonyManager.class). + getSimCountryIso().toUpperCase(Locale.US) : Locale.getDefault().getCountry(); Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(), - TelephonyManager.getDefaultSimCountryIso().toUpperCase(Locale.US), - Leniency.POSSIBLE, Long.MAX_VALUE); + regionCode, Leniency.POSSIBLE, Long.MAX_VALUE); for (PhoneNumberMatch match : matches) { LinkSpec spec = new LinkSpec(); spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString()); diff --git a/core/java/android/util/apk/SourceStampVerifier.java b/core/java/android/util/apk/SourceStampVerifier.java index 759c8649532b..70e4a51bc07a 100644 --- a/core/java/android/util/apk/SourceStampVerifier.java +++ b/core/java/android/util/apk/SourceStampVerifier.java @@ -82,25 +82,34 @@ public abstract class SourceStampVerifier { public static SourceStampVerificationResult verify(String apkFile) { try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { return verify(apk); - } catch (Exception e) { - // Any exception in the SourceStamp verification returns a non-verified SourceStamp - // outcome without affecting the outcome of any of the other signature schemes. - return SourceStampVerificationResult.notVerified(); + } catch (IOException e) { + // Any exception in reading the APK returns a non-present SourceStamp outcome + // without affecting the outcome of any of the other signature schemes. + return SourceStampVerificationResult.notPresent(); } } - private static SourceStampVerificationResult verify(RandomAccessFile apk) - throws IOException, SignatureNotFoundException { - byte[] sourceStampCertificateDigest = getSourceStampCertificateDigest(apk); - if (sourceStampCertificateDigest == null) { - // SourceStamp certificate hash file not found, which means that there is not - // SourceStamp present. + private static SourceStampVerificationResult verify(RandomAccessFile apk) { + byte[] sourceStampCertificateDigest; + try { + sourceStampCertificateDigest = getSourceStampCertificateDigest(apk); + if (sourceStampCertificateDigest == null) { + // SourceStamp certificate hash file not found, which means that there is not + // SourceStamp present. + return SourceStampVerificationResult.notPresent(); + } + } catch (IOException e) { return SourceStampVerificationResult.notPresent(); } - SignatureInfo signatureInfo = - ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID); - Map<Integer, byte[]> apkContentDigests = getApkContentDigests(apk); - return verify(signatureInfo, apkContentDigests, sourceStampCertificateDigest); + + try { + SignatureInfo signatureInfo = + ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID); + Map<Integer, byte[]> apkContentDigests = getApkContentDigests(apk); + return verify(signatureInfo, apkContentDigests, sourceStampCertificateDigest); + } catch (IOException | SignatureNotFoundException e) { + return SourceStampVerificationResult.notVerified(); + } } private static SourceStampVerificationResult verify( diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 3927ebfdb5a8..9f2d36de61cd 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -102,9 +102,9 @@ oneway interface IWindow { void closeSystemDialogs(String reason); /** - * Called for wallpaper windows when their offsets change. + * Called for wallpaper windows when their offsets or zoom level change. */ - void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync); + void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom, boolean sync); void dispatchWallpaperCommand(String action, int x, int y, int z, in Bundle extras, boolean sync); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 91000a93b17c..dfe89a37a229 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -221,6 +221,19 @@ interface IWindowSession { */ void setWallpaperPosition(IBinder windowToken, float x, float y, float xstep, float ystep); + /** + * For wallpaper windows, sets the scale of the wallpaper based on + * SystemUI behavior. + */ + void setWallpaperZoomOut(IBinder windowToken, float scale); + + /** + * For wallpaper windows, sets whether the wallpaper should actually be + * scaled when setWallpaperZoomOut is called. If set to false, the WallpaperService will + * receive the zoom out value but the surface won't be scaled. + */ + void setShouldZoomOutWallpaper(IBinder windowToken, boolean shouldZoom); + @UnsupportedAppUsage void wallpaperOffsetsComplete(IBinder window); diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java index 5d5edecead22..4227f78564a7 100644 --- a/core/java/android/view/InsetsAnimationControlCallbacks.java +++ b/core/java/android/view/InsetsAnimationControlCallbacks.java @@ -16,7 +16,6 @@ package android.view; -import android.view.InsetsController.LayoutInsetsDuringAnimation; import android.view.WindowInsetsAnimation.Bounds; /** @@ -37,7 +36,7 @@ public interface InsetsAnimationControlCallbacks { void startAnimation(InsetsAnimationControlImpl controller, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, - Bounds bounds, @LayoutInsetsDuringAnimation int layoutDuringAnimation); + Bounds bounds); /** * Schedule the apply by posting the animation callback. @@ -46,10 +45,10 @@ public interface InsetsAnimationControlCallbacks { /** * Finish the final steps after the animation. - * @param controller The controller used to control the animation. + * @param runner The runner used to run the animation. * @param shown {@code true} if the insets are shown. */ - void notifyFinished(InsetsAnimationControlImpl controller, boolean shown); + void notifyFinished(InsetsAnimationControlRunner runner, boolean shown); /** * Apply the new params to the surface. @@ -57,4 +56,10 @@ public interface InsetsAnimationControlCallbacks { * apply. */ void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params); + + /** + * Post a message to release the Surface, guaranteed to happen after all + * previous calls to applySurfaceParams. + */ + void releaseSurfaceControlFromRt(SurfaceControl sc); } diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index ae509f3a82c4..baee4123ef47 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -31,9 +31,7 @@ import android.util.ArraySet; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseSetArray; -import android.view.InsetsController.LayoutInsetsDuringAnimation; import android.view.InsetsState.InternalInsetsSide; -import android.view.InsetsState.InternalInsetsType; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation.Bounds; @@ -49,7 +47,8 @@ import java.util.ArrayList; * @hide */ @VisibleForTesting -public class InsetsAnimationControlImpl implements WindowInsetsAnimationController { +public class InsetsAnimationControlImpl implements WindowInsetsAnimationController, + InsetsAnimationControlRunner { private final Rect mTmpFrame = new Rect(); @@ -84,8 +83,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll InsetsState state, WindowInsetsAnimationControlListener listener, @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator, - boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, - @AnimationType int animationType) { + boolean fade, @AnimationType int animationType) { mControls = controls; mListener = listener; mTypes = types; @@ -105,7 +103,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll mAnimation.setAlpha(getCurrentAlpha()); mAnimationType = animationType; mController.startAnimation(this, listener, types, mAnimation, - new Bounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation); + new Bounds(mHiddenInsets, mShownInsets)); } @Override @@ -133,11 +131,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll return mTypes; } - boolean controlsInternalType(@InternalInsetsType int type) { - return InsetsState.toInternalType(mTypes).contains(type); - } - - @AnimationType int getAnimationType() { + @Override + public @AnimationType int getAnimationType() { return mAnimationType; } @@ -185,10 +180,19 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll mAnimation.setAlpha(mPendingAlpha); if (mFinished) { mController.notifyFinished(this, mShownOnFinish); + releaseLeashes(); } return mFinished; } + private void releaseLeashes() { + for (int i = mControls.size() - 1; i >= 0; i--) { + final InsetsSourceControl c = mControls.valueAt(i); + if (c == null) continue; + c.release(mController::releaseSurfaceControlFromRt); + } + } + @Override public void finish(boolean shown) { if (mCancelled || mFinished) { @@ -196,6 +200,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll } setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */); mFinished = true; + mShownOnFinish = shown; } @@ -205,19 +210,23 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll return mAnimation.getFraction(); } - public void onCancelled() { + @Override + public void cancel() { if (mFinished) { return; } mCancelled = true; mListener.onCancelled(); + + releaseLeashes(); } public boolean isCancelled() { return mCancelled; } - WindowInsetsAnimation getAnimation() { + @Override + public WindowInsetsAnimation getAnimation() { return mAnimation; } @@ -225,6 +234,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll return mListener; } + SparseArray<InsetsSourceControl> getControls() { + return mControls; + } + private Insets calculateInsets(InsetsState state, Rect frame, SparseArray<InsetsSourceControl> controls, boolean shown, @Nullable @InternalInsetsSide SparseIntArray typeSideMap) { diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java new file mode 100644 index 000000000000..0711c3e166d8 --- /dev/null +++ b/core/java/android/view/InsetsAnimationControlRunner.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.view.InsetsController.AnimationType; +import android.view.InsetsState.InternalInsetsType; +import android.view.WindowInsets.Type.InsetsType; + +/** + * Interface representing a runner for an insets animation. + * + * @hide + */ +public interface InsetsAnimationControlRunner { + + /** + * @return The {@link InsetsType} the animation of this runner is controlling. + */ + @InsetsType int getTypes(); + + /** + * Cancels the animation. + */ + void cancel(); + + /** + * @return The animation this runner is running. + */ + WindowInsetsAnimation getAnimation(); + + /** + * @return Whether {@link #getTypes()} maps to a specific {@link InternalInsetsType}. + */ + default boolean controlsInternalType(@InternalInsetsType int type) { + return InsetsState.toInternalType(getTypes()).contains(type); + } + + /** + * @return The animation type this runner is running. + */ + @AnimationType int getAnimationType(); +} diff --git a/core/java/android/view/InsetsAnimationThread.java b/core/java/android/view/InsetsAnimationThread.java new file mode 100644 index 000000000000..cdf97333a0ca --- /dev/null +++ b/core/java/android/view/InsetsAnimationThread.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Trace; + +/** + * Thread to be used for inset animations to be running off the main thread. + * @hide + */ +public class InsetsAnimationThread extends HandlerThread { + + private static InsetsAnimationThread sInstance; + private static Handler sHandler; + + private InsetsAnimationThread() { + // TODO: Should this use higher priority? + super("InsetsAnimations"); + } + + private static void ensureThreadLocked() { + if (sInstance == null) { + sInstance = new InsetsAnimationThread(); + sInstance.start(); + sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_VIEW); + sHandler = new Handler(sInstance.getLooper()); + } + } + + public static void release() { + synchronized (InsetsAnimationThread.class) { + if (sInstance == null) { + return; + } + sInstance.getLooper().quitSafely(); + sInstance = null; + sHandler = null; + } + } + + public static InsetsAnimationThread get() { + synchronized (InsetsAnimationThread.class) { + ensureThreadLocked(); + return sInstance; + } + } + + public static Handler getHandler() { + synchronized (InsetsAnimationThread.class) { + ensureThreadLocked(); + return sHandler; + } + } +} diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java new file mode 100644 index 000000000000..13b4cd83b4df --- /dev/null +++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static android.view.SyncRtSurfaceTransactionApplier.applyParams; + +import android.annotation.UiThread; +import android.graphics.Rect; +import android.os.Handler; +import android.util.SparseArray; +import android.view.InsetsController.AnimationType; +import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; +import android.view.WindowInsets.Type.InsetsType; +import android.view.WindowInsetsAnimation.Bounds; +import android.view.animation.Interpolator; + +/** + * Insets animation runner that uses {@link InsetsAnimationThread} to run the animation off from the + * main thread. + * + * @hide + */ +public class InsetsAnimationThreadControlRunner implements InsetsAnimationControlRunner { + + private final InsetsAnimationControlImpl mControl; + private final InsetsAnimationControlCallbacks mOuterCallbacks; + private final Handler mMainThreadHandler; + private final InsetsState mState = new InsetsState(); + private final InsetsAnimationControlCallbacks mCallbacks = + new InsetsAnimationControlCallbacks() { + + private final float[] mTmpFloat9 = new float[9]; + + @Override + @UiThread + public void startAnimation(InsetsAnimationControlImpl controller, + WindowInsetsAnimationControlListener listener, int types, + WindowInsetsAnimation animation, Bounds bounds) { + // Animation will be started in constructor already. + } + + @Override + public void scheduleApplyChangeInsets() { + mControl.applyChangeInsets(mState); + } + + @Override + public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { + releaseControls(mControl.getControls()); + mMainThreadHandler.post(() -> + mOuterCallbacks.notifyFinished(InsetsAnimationThreadControlRunner.this, shown)); + } + + @Override + public void applySurfaceParams(SurfaceParams... params) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + for (int i = params.length - 1; i >= 0; i--) { + SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i]; + applyParams(t, surfaceParams, mTmpFloat9); + } + t.apply(); + t.close(); + } + + @Override + public void releaseSurfaceControlFromRt(SurfaceControl sc) { + // Since we don't push the SurfaceParams to the RT we can release directly + sc.release(); + } + }; + + @UiThread + public InsetsAnimationThreadControlRunner(SparseArray<InsetsSourceControl> controls, Rect frame, + InsetsState state, WindowInsetsAnimationControlListener listener, + @InsetsType int types, + InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator, + boolean fade, @AnimationType int animationType, Handler mainThreadHandler) { + mMainThreadHandler = mainThreadHandler; + mOuterCallbacks = controller; + mControl = new InsetsAnimationControlImpl(copyControls(controls), frame, state, listener, + types, mCallbacks, durationMs, interpolator, fade, animationType); + InsetsAnimationThread.getHandler().post(() -> listener.onReady(mControl, types)); + } + + private void releaseControls(SparseArray<InsetsSourceControl> controls) { + for (int i = controls.size() - 1; i >= 0; i--) { + controls.valueAt(i).release(SurfaceControl::release); + } + } + + private SparseArray<InsetsSourceControl> copyControls( + SparseArray<InsetsSourceControl> controls) { + SparseArray<InsetsSourceControl> copy = new SparseArray<>(controls.size()); + for (int i = 0; i < controls.size(); i++) { + copy.append(controls.keyAt(i), new InsetsSourceControl(controls.valueAt(i))); + } + return copy; + } + + @Override + @UiThread + public int getTypes() { + return mControl.getTypes(); + } + + @Override + @UiThread + public void cancel() { + InsetsAnimationThread.getHandler().post(mControl::cancel); + } + + @Override + @UiThread + public WindowInsetsAnimation getAnimation() { + return mControl.getAnimation(); + } + + @Override + public int getAnimationType() { + return mControl.getAnimationType(); + } +} diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 302d4f3f79c8..88e7f2ed9cf1 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -23,6 +23,7 @@ import static android.view.WindowInsets.Type.ime; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; +import android.animation.AnimationHandler; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -52,6 +53,8 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; +import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -59,6 +62,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.function.BiFunction; /** @@ -165,10 +169,22 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private WindowInsetsAnimationController mController; private ObjectAnimator mAnimator; - protected boolean mShow; + private final boolean mShow; + private final boolean mUseSfVsync; - public InternalAnimationControlListener(boolean show) { + private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = + new ThreadLocal<AnimationHandler>() { + @Override + protected AnimationHandler initialValue() { + AnimationHandler handler = new AnimationHandler(); + handler.setProvider(new SfVsyncFrameCallbackProvider()); + return handler; + } + }; + + public InternalAnimationControlListener(boolean show, boolean useSfVsync) { mShow = show; + mUseSfVsync = useSfVsync; } @Override @@ -191,6 +207,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation onAnimationFinish(); } }); + if (mUseSfVsync) { + mAnimator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get()); + } mAnimator.start(); } @@ -226,12 +245,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation */ private static class RunningAnimation { - RunningAnimation(InsetsAnimationControlImpl control, int type) { - this.control = control; + RunningAnimation(InsetsAnimationControlRunner runner, int type) { + this.runner = runner; this.type = type; } - final InsetsAnimationControlImpl control; + final InsetsAnimationControlRunner runner; final @AnimationType int type; /** @@ -250,7 +269,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener, long durationMs, Interpolator interpolator, @AnimationType int animationType, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, - CancellationSignal cancellationSignal) { + CancellationSignal cancellationSignal, boolean useInsetsAnimationThread) { this.types = types; this.listener = listener; this.durationMs = durationMs; @@ -258,6 +277,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation this.animationType = animationType; this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation; this.cancellationSignal = cancellationSignal; + this.useInsetsAnimationThread = useInsetsAnimationThread; } final @InsetsType int types; @@ -267,6 +287,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final @AnimationType int animationType; final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation; final CancellationSignal cancellationSignal; + final boolean useInsetsAnimationThread; } private final String TAG = "InsetsControllerImpl"; @@ -306,6 +327,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private SyncRtSurfaceTransactionApplier mApplier; private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest; + private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners + = new ArrayList<>(); + + /** Set of inset types for which an animation was started since last resetting this field */ + private @InsetsType int mLastStartedAnimTypes; public InsetsController(ViewRootImpl viewRoot) { this(viewRoot, (controller, type) -> { @@ -340,15 +366,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation InsetsState state = new InsetsState(mState, true /* copySources */); for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { RunningAnimation runningAnimation = mRunningAnimations.get(i); - InsetsAnimationControlImpl control = runningAnimation.control; + InsetsAnimationControlRunner runner = runningAnimation.runner; + if (runner instanceof InsetsAnimationControlImpl) { + InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner; + + // Keep track of running animation to be dispatched. Aggregate it here such that + // if it gets finished within applyChangeInsets we still dispatch it to + // onProgress. + if (runningAnimation.startDispatched) { + mTmpRunningAnims.add(control.getAnimation()); + } - // Keep track of running animation to be dispatched. Aggregate it here such that if - // it gets finished within applyChangeInsets we still dispatch it to onProgress. - if (runningAnimation.startDispatched) { - mTmpRunningAnims.add(control.getAnimation()); - } - if (control.applyChangeInsets(state)) { - mTmpFinishedControls.add(control); + if (control.applyChangeInsets(state)) { + mTmpFinishedControls.add(control); + } } } @@ -459,6 +490,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } mTmpControlArray.clear(); + + // Do not override any animations that the app started in the OnControllableInsetsChanged + // listeners. + int animatingTypes = invokeControllableInsetsChangedListeners(); + showTypes[0] &= ~animatingTypes; + hideTypes[0] &= ~animatingTypes; + if (showTypes[0] != 0) { applyAnimation(showTypes[0], true /* show */, false /* fromIme */); } @@ -485,7 +523,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation pendingRequest.listener, mFrame, true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator, false /* fade */, pendingRequest.animationType, - pendingRequest.layoutInsetsDuringAnimation); + pendingRequest.layoutInsetsDuringAnimation, + pendingRequest.useInsetsAnimationThread); pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel); return; } @@ -542,23 +581,30 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs, @Nullable Interpolator interpolator, @AnimationType int animationType) { - // If the frame of our window doesn't span the entire display, the control API makes very - // little sense, as we don't deal with negative insets. So just cancel immediately. - if (!mState.getDisplayFrame().equals(mFrame)) { + if (!checkDisplayFramesForControlling()) { listener.onCancelled(); CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.cancel(); return cancellationSignal; } return controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator, - false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types)); + false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types), + false /* useInsetsAnimationThread */); + } + + private boolean checkDisplayFramesForControlling() { + + // If the frame of our window doesn't span the entire display, the control API makes very + // little sense, as we don't deal with negative insets. So just cancel immediately. + return mState.getDisplayFrame().equals(mFrame); } private CancellationSignal controlAnimationUnchecked(@InsetsType int types, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, long durationMs, Interpolator interpolator, boolean fade, @AnimationType int animationType, - @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { + @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, + boolean useInsetsAnimationThread) { CancellationSignal cancellationSignal = new CancellationSignal(); if (types == 0) { // nothing to animate. @@ -567,6 +613,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return cancellationSignal; } cancelExistingControllers(types); + mLastStartedAnimTypes |= types; final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); final SparseArray<InsetsSourceControl> controls = new SparseArray<>(); @@ -580,7 +627,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation abortPendingImeControlRequest(); final PendingControlRequest request = new PendingControlRequest(types, listener, durationMs, - interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal); + interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal, + useInsetsAnimationThread); mPendingImeControlRequest = request; mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS); cancellationSignal.setOnCancelListener(() -> { @@ -597,11 +645,21 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return cancellationSignal; } - final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls, - frame, mState, listener, typesReady, this, durationMs, interpolator, fade, - layoutInsetsDuringAnimation, animationType); - mRunningAnimations.add(new RunningAnimation(controller, animationType)); - cancellationSignal.setOnCancelListener(controller::onCancelled); + + final InsetsAnimationControlRunner runner = useInsetsAnimationThread + ? new InsetsAnimationThreadControlRunner(controls, + frame, mState, listener, typesReady, this, durationMs, interpolator, fade, + animationType, mViewRoot.mHandler) + : new InsetsAnimationControlImpl(controls, + frame, mState, listener, typesReady, this, durationMs, interpolator, fade, + animationType); + mRunningAnimations.add(new RunningAnimation(runner, animationType)); + cancellationSignal.setOnCancelListener(runner::cancel); + if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) { + showDirectly(types); + } else { + hideDirectly(types, false /* animationFinished */, animationType); + } return cancellationSignal; } @@ -646,7 +704,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } final InsetsSourceControl control = consumer.getControl(); if (control != null) { - controls.put(consumer.getType(), control); + controls.put(consumer.getType(), new InsetsSourceControl(control)); typesReady |= toPublicType(consumer.getType()); } else if (animationType == ANIMATION_TYPE_SHOW) { @@ -685,7 +743,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void cancelExistingControllers(@InsetsType int types) { for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { - InsetsAnimationControlImpl control = mRunningAnimations.get(i).control; + InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; if ((control.getTypes() & types) != 0) { cancelAnimation(control, true /* invokeCallback */); } @@ -705,13 +763,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting @Override - public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) { - cancelAnimation(controller, false /* invokeCallback */); + public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { + cancelAnimation(runner, false /* invokeCallback */); if (shown) { - showDirectly(controller.getTypes()); + showDirectly(runner.getTypes()); } else { - hideDirectly(controller.getTypes(), true /* animationFinished */, - controller.getAnimationType()); + hideDirectly(runner.getTypes(), true /* animationFinished */, + runner.getAnimationType()); } } @@ -736,7 +794,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation void notifyControlRevoked(InsetsSourceConsumer consumer) { for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { - InsetsAnimationControlImpl control = mRunningAnimations.get(i).control; + InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; if ((control.getTypes() & toPublicType(consumer.getType())) != 0) { cancelAnimation(control, true /* invokeCallback */); } @@ -746,12 +804,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } - private void cancelAnimation(InsetsAnimationControlImpl control, boolean invokeCallback) { + private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) { if (invokeCallback) { - control.onCancelled(); + control.cancel(); } for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { - if (mRunningAnimations.get(i).control == control) { + if (mRunningAnimations.get(i).runner == control) { mRunningAnimations.remove(i); break; } @@ -824,7 +882,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting public @AnimationType int getAnimationType(@InternalInsetsType int type) { for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { - InsetsAnimationControlImpl control = mRunningAnimations.get(i).control; + InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; if (control.controlsInternalType(type)) { return mRunningAnimations.get(i).type; } @@ -858,15 +916,24 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return; } + boolean useInsetsAnimationThread = canUseInsetsAnimationThread(); final InternalAnimationControlListener listener = - new InternalAnimationControlListener(show); + new InternalAnimationControlListener(show, useInsetsAnimationThread); // Show/hide animations always need to be relative to the display frame, in order that shown // and hidden state insets are correct. controlAnimationUnchecked( types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(), INTERPOLATOR, true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN - : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN); + : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, + useInsetsAnimationThread); + } + + private boolean canUseInsetsAnimationThread() { + if (mViewRoot.mView == null) { + return true; + } + return !mViewRoot.mView.hasWindowInsetsAnimationCallback(); } private void hideDirectly( @@ -901,13 +968,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void startAnimation(InsetsAnimationControlImpl controller, WindowInsetsAnimationControlListener listener, int types, - WindowInsetsAnimation animation, Bounds bounds, int layoutDuringAnimation) { - if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) { - showDirectly(types); - } else { - hideDirectly(types, false /* animationFinished */, controller.getAnimationType()); - } - + WindowInsetsAnimation animation, Bounds bounds) { if (mViewRoot.mView == null) { return; } @@ -921,7 +982,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { RunningAnimation runningAnimation = mRunningAnimations.get(i); - if (runningAnimation.control == controller) { + if (runningAnimation.runner == controller) { runningAnimation.startDispatched = true; } } @@ -994,6 +1055,48 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return mViewRoot.mWindowAttributes.insetsFlags.behavior; } + private @InsetsType int calculateControllableTypes() { + if (!checkDisplayFramesForControlling()) { + return 0; + } + @InsetsType int result = 0; + for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { + InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); + if (consumer.getControl() != null) { + result |= toPublicType(consumer.mType); + } + } + return result; + } + + /** + * @return The types that are now animating due to a listener invoking control/show/hide + */ + private @InsetsType int invokeControllableInsetsChangedListeners() { + mLastStartedAnimTypes = 0; + @InsetsType int types = calculateControllableTypes(); + int size = mControllableInsetsChangedListeners.size(); + for (int i = 0; i < size; i++) { + mControllableInsetsChangedListeners.get(i).onControllableInsetsChanged(this, types); + } + return mLastStartedAnimTypes; + } + + @Override + public void addOnControllableInsetsChangedListener( + OnControllableInsetsChangedListener listener) { + Objects.requireNonNull(listener); + mControllableInsetsChangedListeners.add(listener); + listener.onControllableInsetsChanged(this, calculateControllableTypes()); + } + + @Override + public void removeOnControllableInsetsChangedListener( + OnControllableInsetsChangedListener listener) { + Objects.requireNonNull(listener); + mControllableInsetsChangedListeners.remove(listener); + } + /** * At the time we receive new leashes (e.g. InsetsSourceConsumer is processing * setControl) we need to release the old leash. But we may have already scheduled diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 252fc0c82cf7..332573449e18 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -117,7 +117,7 @@ public class InsetsSourceConsumer { } } if (lastControl != null) { - lastControl.release(mController); + lastControl.release(mController::releaseSurfaceControlFromRt); } } diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index 75f6eab798ce..f3ec65f997ba 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -22,6 +22,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.view.InsetsState.InternalInsetsType; +import java.util.function.Consumer; + /** * Represents a parcelable object to allow controlling a single {@link InsetsSource}. * @hide @@ -94,9 +96,9 @@ public class InsetsSourceControl implements Parcelable { dest.writeParcelable(mSurfacePosition, 0 /* flags*/); } - public void release(InsetsController controller) { + public void release(Consumer<SurfaceControl> surfaceReleaseConsumer) { if (mLeash != null) { - controller.releaseSurfaceControlFromRt(mLeash); + surfaceReleaseConsumer.accept(mLeash); } } diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index 8ec5df85dc7b..18e0132e2c4e 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -35,6 +35,7 @@ import android.widget.RemoteViews; import com.android.internal.R; import com.android.internal.widget.CachingIconView; +import com.android.internal.widget.NotificationExpandButton; import java.util.ArrayList; @@ -56,7 +57,7 @@ public class NotificationHeaderView extends ViewGroup { private OnClickListener mAppOpsListener; private HeaderTouchListener mTouchListener = new HeaderTouchListener(); private LinearLayout mTransferChip; - private ImageView mExpandButton; + private NotificationExpandButton mExpandButton; private CachingIconView mIcon; private View mProfileBadge; private View mOverlayIcon; @@ -65,7 +66,6 @@ public class NotificationHeaderView extends ViewGroup { private View mAppOps; private View mAudiblyAlertedIcon; private int mIconColor; - private int mOriginalNotificationColor; private boolean mExpanded; private boolean mShowExpandButtonAtEnd; private boolean mShowWorkBadgeAtEnd; @@ -324,13 +324,8 @@ public class NotificationHeaderView extends ViewGroup { return mIconColor; } - @RemotableViewMethod - public void setOriginalNotificationColor(int color) { - mOriginalNotificationColor = color; - } - public int getOriginalNotificationColor() { - return mOriginalNotificationColor; + return mExpandButton.getOriginalNotificationColor(); } @RemotableViewMethod @@ -371,7 +366,7 @@ public class NotificationHeaderView extends ViewGroup { contentDescriptionId = R.string.expand_button_content_description_collapsed; } mExpandButton.setImageDrawable(getContext().getDrawable(drawableId)); - mExpandButton.setColorFilter(mOriginalNotificationColor); + mExpandButton.setColorFilter(getOriginalNotificationColor()); mExpandButton.setContentDescription(mContext.getText(contentDescriptionId)); } diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java index c0ed9359c613..7f3641803770 100644 --- a/core/java/android/view/PendingInsetsController.java +++ b/core/java/android/view/PendingInsetsController.java @@ -38,6 +38,8 @@ public class PendingInsetsController implements WindowInsetsController { private @Behavior int mBehavior = KEEP_BEHAVIOR; private final InsetsState mDummyState = new InsetsState(); private InsetsController mReplayedInsetsController; + private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners + = new ArrayList<>(); @Override public void show(int types) { @@ -112,6 +114,27 @@ public class PendingInsetsController implements WindowInsetsController { return mDummyState; } + @Override + public void addOnControllableInsetsChangedListener( + OnControllableInsetsChangedListener listener) { + if (mReplayedInsetsController != null) { + mReplayedInsetsController.addOnControllableInsetsChangedListener(listener); + } else { + mControllableInsetsChangedListeners.add(listener); + listener.onControllableInsetsChanged(this, 0); + } + } + + @Override + public void removeOnControllableInsetsChangedListener( + OnControllableInsetsChangedListener listener) { + if (mReplayedInsetsController != null) { + mReplayedInsetsController.removeOnControllableInsetsChangedListener(listener); + } else { + mControllableInsetsChangedListeners.remove(listener); + } + } + /** * Replays the commands on {@code controller} and attaches it to this instance such that any * calls will be forwarded to the real instance in the future. @@ -128,9 +151,15 @@ public class PendingInsetsController implements WindowInsetsController { for (int i = 0; i < size; i++) { mRequests.get(i).replay(controller); } + size = mControllableInsetsChangedListeners.size(); + for (int i = 0; i < size; i++) { + controller.addOnControllableInsetsChangedListener( + mControllableInsetsChangedListeners.get(i)); + } // Reset all state so it doesn't get applied twice just in case mRequests.clear(); + mControllableInsetsChangedListeners.clear(); mBehavior = KEEP_BEHAVIOR; mAppearance = 0; mAppearanceMask = 0; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6236e6ea31e9..708a09467247 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3318,7 +3318,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Flag indicating that the view is autofilled * * @see #isAutofilled() - * @see #setAutofilled(boolean) + * @see #setAutofilled(boolean, boolean) */ private static final int PFLAG3_IS_AUTOFILLED = 0x10000; @@ -3428,6 +3428,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE * 11 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK * 1 PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS + * 1 PFLAG4_AUTOFILL_HIDE_HIGHLIGHT * |-------|-------|-------|-------| */ @@ -3470,6 +3471,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static final int PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS = 0x000000100; + /** + * Flag indicating the field should not have yellow highlight when autofilled. + */ + private static final int PFLAG4_AUTOFILL_HIDE_HIGHLIGHT = 0x100; + /* End of masks for mPrivateFlags4 */ /** @hide */ @@ -9170,6 +9176,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide + */ + public boolean hideAutofillHighlight() { + return (mPrivateFlags4 & PFLAG4_AUTOFILL_HIDE_HIGHLIGHT) != 0; + } + + /** * Gets the {@link View}'s current autofill value. * * <p>By default returns {@code null}, but subclasses should override it and return an @@ -11227,6 +11240,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @return {@code true} if any {@link WindowInsetsAnimation.Callback} is registered on the view + * or view tree of the sub-hierarchy {@code false} otherwise. + * @hide + */ + public boolean hasWindowInsetsAnimationCallback() { + return getListenerInfo().mWindowInsetsAnimationCallback != null; + } + + /** * Dispatches {@link WindowInsetsAnimation.Callback#onPrepare(WindowInsetsAnimation)} * when Window Insets animation is being prepared. * @param animation current animation @@ -11741,7 +11763,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ @TestApi - public void setAutofilled(boolean isAutofilled) { + public void setAutofilled(boolean isAutofilled, boolean hideHighlight) { boolean wasChanged = isAutofilled != isAutofilled(); if (wasChanged) { @@ -11751,6 +11773,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags3 &= ~PFLAG3_IS_AUTOFILLED; } + if (hideHighlight) { + mPrivateFlags4 |= PFLAG4_AUTOFILL_HIDE_HIGHLIGHT; + } else { + mPrivateFlags4 &= ~PFLAG4_AUTOFILL_HIDE_HIGHLIGHT; + } + invalidate(); } } @@ -20569,6 +20597,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, state.mStartActivityRequestWhoSaved = mStartActivityRequestWho; state.mIsAutofilled = isAutofilled(); + state.mHideHighlight = hideAutofillHighlight(); state.mAutofillViewId = mAutofillViewId; return state; } @@ -20645,7 +20674,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mStartActivityRequestWho = baseState.mStartActivityRequestWhoSaved; } if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) { - setAutofilled(baseState.mIsAutofilled); + setAutofilled(baseState.mIsAutofilled, baseState.mHideHighlight); } if ((baseState.mSavedData & BaseSavedState.AUTOFILL_ID) != 0) { // It can happen that views have the same view id and the restoration path will not @@ -24078,12 +24107,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Draw {@link View#isAutofilled()} highlight over view if the view is autofilled. + * Draw {@link View#isAutofilled()} highlight over view if the view is autofilled, unless + * {@link #PFLAG4_AUTOFILL_HIDE_HIGHLIGHT} is enabled. * * @param canvas The canvas to draw on */ private void drawAutofilledHighlight(@NonNull Canvas canvas) { - if (isAutofilled()) { + if (isAutofilled() && !hideAutofillHighlight()) { Drawable autofilledHighlight = getAutofilledDrawable(); if (autofilledHighlight != null) { @@ -28526,6 +28556,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mSavedData; String mStartActivityRequestWhoSaved; boolean mIsAutofilled; + boolean mHideHighlight; int mAutofillViewId; /** @@ -28549,6 +28580,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mSavedData = source.readInt(); mStartActivityRequestWhoSaved = source.readString(); mIsAutofilled = source.readBoolean(); + mHideHighlight = source.readBoolean(); mAutofillViewId = source.readInt(); } @@ -28568,6 +28600,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, out.writeInt(mSavedData); out.writeString(mStartActivityRequestWhoSaved); out.writeBoolean(mIsAutofilled); + out.writeBoolean(mHideHighlight); out.writeInt(mAutofillViewId); } @@ -29066,8 +29099,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mTreeObserver = new ViewTreeObserver(context); } + @Nullable + ContentCaptureManager getContentCaptureManager(@NonNull Context context) { + if (mContentCaptureManager != null) { + return mContentCaptureManager; + } + mContentCaptureManager = context.getSystemService(ContentCaptureManager.class); + return mContentCaptureManager; + } + + void delayNotifyContentCaptureInsetsEvent(@NonNull Insets insets) { + if (mContentCaptureManager == null) { + return; + } + + ArrayList<Object> events = ensureEvents( + mContentCaptureManager.getMainContentCaptureSession()); + events.add(insets); + } + private void delayNotifyContentCaptureEvent(@NonNull ContentCaptureSession session, @NonNull View view, boolean appeared) { + ArrayList<Object> events = ensureEvents(session); + events.add(appeared ? view : view.getAutofillId()); + } + + @NonNull + private ArrayList<Object> ensureEvents(@NonNull ContentCaptureSession session) { if (mContentCaptureEvents == null) { // Most of the time there will be just one session, so intial capacity is 1 mContentCaptureEvents = new SparseArray<>(1); @@ -29079,16 +29137,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, events = new ArrayList<>(); mContentCaptureEvents.put(sessionId, events); } - events.add(appeared ? view : view.getAutofillId()); - } - @Nullable - ContentCaptureManager getContentCaptureManager(@NonNull Context context) { - if (mContentCaptureManager != null) { - return mContentCaptureManager; - } - mContentCaptureManager = context.getSystemService(ContentCaptureManager.class); - return mContentCaptureManager; + return events; } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 7d4ec3dcf2e7..e34e84c977ea 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -7258,6 +7258,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager : DISPATCH_MODE_CONTINUE_ON_SUBTREE; } + /** + * @hide + */ + @Override + public boolean hasWindowInsetsAnimationCallback() { + if (super.hasWindowInsetsAnimationCallback()) { + return true; + } + + // If we are root-level content view that fits insets, we imitate consuming behavior, so + // no child will retrieve window insets animation callback. + // See dispatchWindowInsetsAnimationPrepare. + boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0 + || isFrameworkOptionalFitsSystemWindows(); + if (isOptionalFitSystemWindows && mAttachInfo != null + && mAttachInfo.mContentOnApplyWindowInsetsListener != null) { + return false; + } + + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + if (getChildAt(i).hasWindowInsetsAnimationCallback()) { + return true; + } + } + return false; + } + @Override public void dispatchWindowInsetsAnimationPrepare( @NonNull WindowInsetsAnimation animation) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 9228fbdf82c8..dd34bcb018b9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -81,6 +81,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.FrameInfo; import android.graphics.HardwareRenderer.FrameDrawingCallback; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.Point; @@ -2254,6 +2255,7 @@ public final class ViewRootImpl implements ViewParent, insets = insets.consumeDisplayCutout(); } host.dispatchApplyWindowInsets(insets); + mAttachInfo.delayNotifyContentCaptureInsetsEvent(insets.getInsets(Type.all())); Trace.traceEnd(Trace.TRACE_TAG_VIEW); } @@ -3118,6 +3120,8 @@ public final class ViewRootImpl implements ViewParent, ViewStructure structure = session.newViewStructure(view); view.onProvideContentCaptureStructure(structure, /* flags= */ 0); session.notifyViewAppeared(structure); + } else if (event instanceof Insets) { + mainSession.notifyViewInsetsChanged(sessionId, (Insets) event); } else { Log.w(mTag, "invalid content capture event: " + event); } @@ -5699,9 +5703,9 @@ public final class ViewRootImpl implements ViewParent, mTranslator.translateEventInScreenToAppWindow(event); } - // Enter touch mode if event is coming from a touch screen device. + // Enter touch mode on down or scroll from any type of a device. final int action = event.getAction(); - if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) { + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { ensureTouchMode(true); } @@ -9054,7 +9058,7 @@ public final class ViewRootImpl implements ViewParent, @Override public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, - boolean sync) { + float zoom, boolean sync) { if (sync) { try { mWindowSession.wallpaperOffsetsComplete(asBinder()); diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java index e05c3743565c..9c16e1334e00 100644 --- a/core/java/android/view/WindowContainerTransaction.java +++ b/core/java/android/view/WindowContainerTransaction.java @@ -131,6 +131,31 @@ public class WindowContainerTransaction implements Parcelable { } /** + * Set the windowing mode of children of a given root task, without changing + * the windowing mode of the Task itself. This can be used during transitions + * for example to make the activity render it's fullscreen configuration + * while the Task is still in PIP, so you can complete the animation. + * + * TODO(b/134365562): Can be removed once TaskOrg drives full-screen + */ + public WindowContainerTransaction setActivityWindowingMode(IWindowContainer container, + int windowingMode) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mActivityWindowingMode = windowingMode; + return this; + } + + /** + * Sets the windowing mode of the given container. + */ + public WindowContainerTransaction setWindowingMode(IWindowContainer container, + int windowingMode) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mWindowingMode = windowingMode; + return this; + } + + /** * Sets whether a container or any of its children can be focusable. When {@code false}, no * child can be focused; however, when {@code true}, it is still possible for children to be * non-focusable due to WM policy. @@ -235,6 +260,9 @@ public class WindowContainerTransaction implements Parcelable { private Rect mPinnedBounds = null; private SurfaceControl.Transaction mBoundsChangeTransaction = null; + private int mActivityWindowingMode = -1; + private int mWindowingMode = -1; + public Change() {} protected Change(Parcel in) { @@ -251,6 +279,17 @@ public class WindowContainerTransaction implements Parcelable { mBoundsChangeTransaction = SurfaceControl.Transaction.CREATOR.createFromParcel(in); } + + mWindowingMode = in.readInt(); + mActivityWindowingMode = in.readInt(); + } + + public int getWindowingMode() { + return mWindowingMode; + } + + public int getActivityWindowingMode() { + return mActivityWindowingMode; } public Configuration getConfiguration() { @@ -340,6 +379,9 @@ public class WindowContainerTransaction implements Parcelable { if (mBoundsChangeTransaction != null) { mBoundsChangeTransaction.writeToParcel(dest, flags); } + + dest.writeInt(mWindowingMode); + dest.writeInt(mActivityWindowingMode); } @Override diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index b7ca03798bbe..2ad557e6d9f6 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -20,7 +20,9 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Insets; +import android.inputmethodservice.InputMethodService; import android.os.CancellationSignal; +import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.animation.Interpolator; @@ -212,4 +214,55 @@ public interface WindowInsetsController { * @hide */ InsetsState getState(); + + /** + * Adds a {@link OnControllableInsetsChangedListener} to the window insets controller. + * + * @param listener The listener to add. + * + * @see OnControllableInsetsChangedListener + * @see #removeOnControllableInsetsChangedListener(OnControllableInsetsChangedListener) + */ + void addOnControllableInsetsChangedListener( + @NonNull OnControllableInsetsChangedListener listener); + + /** + * Removes a {@link OnControllableInsetsChangedListener} from the window insets controller. + * + * @param listener The listener to remove. + * + * @see OnControllableInsetsChangedListener + * @see #addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener) + */ + void removeOnControllableInsetsChangedListener( + @NonNull OnControllableInsetsChangedListener listener); + + /** + * Listener to be notified when the set of controllable {@link InsetsType} controlled by a + * {@link WindowInsetsController} changes. + * <p> + * Once a {@link InsetsType} becomes controllable, the app will be able to control the window + * that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}. + * <p> + * Note: When listening to controllability of the {@link Type#ime}, + * {@link #controlWindowInsetsAnimation} may still fail in case the {@link InputMethodService} + * decides to cancel the show request. This could happen when there is a hardware keyboard + * attached. + * + * @see #addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener) + * @see #removeOnControllableInsetsChangedListener(OnControllableInsetsChangedListener) + */ + interface OnControllableInsetsChangedListener { + + /** + * Called when the set of controllable {@link InsetsType} changes. + * + * @param controller The controller for which the set of controllable {@link InsetsType}s + * are changing. + * @param typeMask Bitwise type-mask of the {@link InsetsType}s the controller is currently + * able to control. + */ + void onControllableInsetsChanged(@NonNull WindowInsetsController controller, + @InsetsType int typeMask); + } } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 867b648cfb10..c5fa3c83a0e5 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -805,6 +805,7 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_APPLICATION_OVERLAY, to = "APPLICATION_OVERLAY") }) + @WindowType public int type; /** @@ -1244,13 +1245,47 @@ public interface WindowManager extends ViewManager { public static final int INVALID_WINDOW_TYPE = -1; /** + * @hide + */ + @IntDef(prefix = "TYPE_", value = { + TYPE_ACCESSIBILITY_OVERLAY, + TYPE_APPLICATION, + TYPE_APPLICATION_ATTACHED_DIALOG, + TYPE_APPLICATION_MEDIA, + TYPE_APPLICATION_OVERLAY, + TYPE_APPLICATION_PANEL, + TYPE_APPLICATION_STARTING, + TYPE_APPLICATION_SUB_PANEL, + TYPE_BASE_APPLICATION, + TYPE_DRAWN_APPLICATION, + TYPE_INPUT_METHOD, + TYPE_INPUT_METHOD_DIALOG, + TYPE_KEYGUARD, + TYPE_KEYGUARD_DIALOG, + TYPE_PHONE, + TYPE_PRIORITY_PHONE, + TYPE_PRIVATE_PRESENTATION, + TYPE_SEARCH_BAR, + TYPE_STATUS_BAR, + TYPE_STATUS_BAR_PANEL, + TYPE_SYSTEM_ALERT, + TYPE_SYSTEM_DIALOG, + TYPE_SYSTEM_ERROR, + TYPE_SYSTEM_OVERLAY, + TYPE_TOAST, + TYPE_WALLPAPER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WindowType {} + + /** * Return true if the window type is an alert window. * * @param type The window type. * @return If the window type is an alert window. * @hide */ - public static boolean isSystemAlertWindowType(int type) { + public static boolean isSystemAlertWindowType(@WindowType int type) { switch (type) { case TYPE_PHONE: case TYPE_PRIORITY_PHONE: diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 01a1c77d50af..410d9afe73da 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -504,6 +504,7 @@ public final class WindowManagerGlobal { } void doRemoveView(ViewRootImpl root) { + boolean allViewsRemoved; synchronized (mLock) { final int index = mRoots.indexOf(root); if (index >= 0) { @@ -512,10 +513,17 @@ public final class WindowManagerGlobal { final View view = mViews.remove(index); mDyingViews.remove(view); } + allViewsRemoved = mRoots.isEmpty(); } if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) { doTrimForeground(); } + + // If we don't have any views anymore in our process, we no longer need the + // InsetsAnimationThread to save some resources. + if (allViewsRemoved) { + InsetsAnimationThread.release(); + } } private int findViewLocked(View view, boolean required) { diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 87dcba0490ee..144f8e3a7108 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -312,6 +312,14 @@ public class WindowlessWindowManager implements IWindowSession { } @Override + public void setWallpaperZoomOut(android.os.IBinder windowToken, float zoom) { + } + + @Override + public void setShouldZoomOutWallpaper(android.os.IBinder windowToken, boolean shouldZoom) { + } + + @Override public void wallpaperOffsetsComplete(android.os.IBinder window) { } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index ce7cfa71e9a9..39a9ed4a82e7 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -17,6 +17,7 @@ package android.view.autofill; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; +import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sVerbose; import static android.view.autofill.Helper.toList; @@ -63,6 +64,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityWindowInfo; import android.widget.EditText; +import android.widget.TextView; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; @@ -983,6 +985,10 @@ public final class AutofillManager { if (!isClientDisablingEnterExitEvent()) { final AutofillValue value = view.getAutofillValue(); + if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) { + flags |= FLAG_PASSWORD_INPUT_TYPE; + } + if (!isActiveLocked()) { // Starts new session. startSessionLocked(id, null, value, flags); @@ -1149,6 +1155,10 @@ public final class AutofillManager { } else { // don't notify entered when Activity is already in background if (!isClientDisablingEnterExitEvent()) { + if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) { + flags |= FLAG_PASSWORD_INPUT_TYPE; + } + if (!isActiveLocked()) { // Starts new session. startSessionLocked(id, bounds, null, flags); @@ -1226,7 +1236,7 @@ public final class AutofillManager { // If the session is gone some fields might still be highlighted, hence we have to // remove the isAutofilled property even if no sessions are active. if (mLastAutofilledData == null) { - view.setAutofilled(false); + view.setAutofilled(false, false); } else { id = view.getAutofillId(); if (mLastAutofilledData.containsKey(id)) { @@ -1234,13 +1244,13 @@ public final class AutofillManager { valueWasRead = true; if (Objects.equals(mLastAutofilledData.get(id), value)) { - view.setAutofilled(true); + view.setAutofilled(true, false); } else { - view.setAutofilled(false); + view.setAutofilled(false, false); mLastAutofilledData.remove(id); } } else { - view.setAutofilled(false); + view.setAutofilled(false, false); } } @@ -2156,7 +2166,8 @@ public final class AutofillManager { * @param view The view that is to be autofilled * @param targetValue The value we want to fill into view */ - private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) { + private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue, + boolean hideHighlight) { AutofillValue currentValue = view.getAutofillValue(); if (Objects.equals(currentValue, targetValue)) { synchronized (mLock) { @@ -2165,11 +2176,12 @@ public final class AutofillManager { } mLastAutofilledData.put(view.getAutofillId(), targetValue); } - view.setAutofilled(true); + view.setAutofilled(true, hideHighlight); } } - private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { + private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values, + boolean hideHighlight) { synchronized (mLock) { if (sessionId != mSessionId) { return; @@ -2228,7 +2240,7 @@ public final class AutofillManager { // synchronously. // If autofill happens async, the view is set to autofilled in // notifyValueChanged. - setAutofilledIfValuesIs(view, value); + setAutofilledIfValuesIs(view, value, hideHighlight); numApplied++; } @@ -3246,10 +3258,11 @@ public final class AutofillManager { } @Override - public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { + public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values, + boolean hideHighlight) { final AutofillManager afm = mAfm.get(); if (afm != null) { - afm.post(() -> afm.autofill(sessionId, ids, values)); + afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight)); } } @@ -3387,10 +3400,11 @@ public final class AutofillManager { } @Override - public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { + public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values, + boolean hideHighlight) { final AutofillManager afm = mAfm.get(); if (afm != null) { - afm.post(() -> afm.autofill(sessionId, ids, values)); + afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight)); } } diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl index 03054dfdc7ea..8526c1e443c8 100644 --- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl +++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl @@ -38,7 +38,8 @@ interface IAugmentedAutofillManagerClient { /** * Autofills the activity with the contents of the values. */ - void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values); + void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values, + boolean hideHighlight); /** * Requests showing the fill UI. diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index 3903665f2cde..4371b3c54f94 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -44,7 +44,8 @@ oneway interface IAutoFillManagerClient { /** * Autofills the activity with the contents of a dataset. */ - void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values); + void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values, + boolean hideHighlight); /** * Authenticates a fill response or a data set. diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java index 7487ec4d921c..44b4353871a2 100644 --- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java @@ -17,6 +17,7 @@ package android.view.contentcapture; import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.Insets; import android.view.autofill.AutofillId; import android.view.contentcapture.ViewNode.ViewStructureImpl; @@ -84,6 +85,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession { } @Override + void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) { + getMainCaptureSession().notifyViewInsetsChanged(mId, viewInsets); + } + + @Override public void internalNotifyViewTreeEvent(boolean started) { getMainCaptureSession().notifyViewTreeEvent(mId, started); } diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index c29d251e6535..ea34d948c91a 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.graphics.Insets; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -112,6 +113,11 @@ public final class ContentCaptureEvent implements Parcelable { */ public static final int TYPE_SESSION_PAUSED = 8; + /** + * Called when the view's insets are changed. The new insets associated with the + * event may then be retrieved by calling {@link #getInsets()} + */ + public static final int TYPE_VIEW_INSETS_CHANGED = 9; /** @hide */ @IntDef(prefix = { "TYPE_" }, value = { @@ -122,7 +128,8 @@ public final class ContentCaptureEvent implements Parcelable { TYPE_VIEW_TREE_APPEARED, TYPE_CONTEXT_UPDATED, TYPE_SESSION_PAUSED, - TYPE_SESSION_RESUMED + TYPE_SESSION_RESUMED, + TYPE_VIEW_INSETS_CHANGED }) @Retention(RetentionPolicy.SOURCE) public @interface EventType{} @@ -136,6 +143,7 @@ public final class ContentCaptureEvent implements Parcelable { private @Nullable CharSequence mText; private int mParentSessionId = NO_SESSION_ID; private @Nullable ContentCaptureContext mClientContext; + private @Nullable Insets mInsets; /** @hide */ public ContentCaptureEvent(int sessionId, int type, long eventTime) { @@ -242,6 +250,13 @@ public final class ContentCaptureEvent implements Parcelable { return this; } + /** @hide */ + @NonNull + public ContentCaptureEvent setInsets(@NonNull Insets insets) { + mInsets = insets; + return this; + } + /** * Gets the type of the event. * @@ -305,6 +320,16 @@ public final class ContentCaptureEvent implements Parcelable { } /** + * Gets the rectangle of the insets associated with the event. Valid insets will only be + * returned if the type of the event is {@link #TYPE_VIEW_INSETS_CHANGED}, otherwise they + * will be null. + */ + @Nullable + public Insets getInsets() { + return mInsets; + } + + /** * Merges event of the same type, either {@link #TYPE_VIEW_TEXT_CHANGED} * or {@link #TYPE_VIEW_DISAPPEARED}. * @@ -369,7 +394,9 @@ public final class ContentCaptureEvent implements Parcelable { } if (mClientContext != null) { pw.print(", context="); mClientContext.dump(pw); pw.println(); - + } + if (mInsets != null) { + pw.print(", insets="); pw.println(mInsets); } } @@ -401,6 +428,9 @@ public final class ContentCaptureEvent implements Parcelable { if (mClientContext != null) { string.append(", context=").append(mClientContext); } + if (mInsets != null) { + string.append(", insets=").append(mInsets); + } return string.append(']').toString(); } @@ -424,6 +454,9 @@ public final class ContentCaptureEvent implements Parcelable { if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) { parcel.writeParcelable(mClientContext, flags); } + if (mType == TYPE_VIEW_INSETS_CHANGED) { + parcel.writeParcelable(mInsets, flags); + } } public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureEvent> CREATOR = @@ -455,6 +488,9 @@ public final class ContentCaptureEvent implements Parcelable { if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) { event.setClientContext(parcel.readParcelable(null)); } + if (type == TYPE_VIEW_INSETS_CHANGED) { + event.setInsets(parcel.readParcelable(null)); + } return event; } @@ -488,6 +524,8 @@ public final class ContentCaptureEvent implements Parcelable { return "VIEW_TREE_APPEARED"; case TYPE_CONTEXT_UPDATED: return "CONTEXT_UPDATED"; + case TYPE_VIEW_INSETS_CHANGED: + return "VIEW_INSETS_CHANGED"; default: return "UKNOWN_TYPE: " + type; } diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 2134dab7986b..012f5e6d3507 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -23,6 +23,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.graphics.Insets; import android.util.DebugUtils; import android.util.Log; import android.view.View; @@ -440,6 +441,19 @@ public abstract class ContentCaptureSession implements AutoCloseable { abstract void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text); + /** + * Notifies the Intelligence Service that the insets of a view have changed. + */ + public final void notifyViewInsetsChanged(@NonNull Insets viewInsets) { + Preconditions.checkNotNull(viewInsets); + + if (!isContentCaptureEnabled()) return; + + internalNotifyViewInsetsChanged(viewInsets); + } + + abstract void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets); + /** @hide */ public abstract void internalNotifyViewTreeEvent(boolean started); diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 96f224fef251..893d38dcfde7 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -22,6 +22,7 @@ import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_RESUM import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_INSETS_CHANGED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING; @@ -36,6 +37,7 @@ import android.annotation.UiThread; import android.content.ComponentName; import android.content.Context; import android.content.pm.ParceledListSlice; +import android.graphics.Insets; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -578,6 +580,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } @Override + void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) { + notifyViewInsetsChanged(mId, viewInsets); + } + + @Override public void internalNotifyViewTreeEvent(boolean started) { notifyViewTreeEvent(mId, started); } @@ -642,6 +649,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } /** Public because is also used by ViewRootImpl */ + public void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) { + sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED) + .setInsets(viewInsets)); + } + + /** Public because is also used by ViewRootImpl */ public void notifyViewTreeEvent(int sessionId, boolean started) { final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED; sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH); diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java index 650061396992..dd1738a5ff29 100644 --- a/core/java/android/view/inputmethod/InlineSuggestion.java +++ b/core/java/android/view/inputmethod/InlineSuggestion.java @@ -16,6 +16,7 @@ package android.view.inputmethod; +import android.annotation.BinderThread; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; @@ -94,19 +95,21 @@ public final class InlineSuggestion implements Parcelable { } - /** * Inflates a view with the content of this suggestion at a specific size. * The size must be between the {@link InlinePresentationSpec#getMinSize() min size} * and the {@link InlinePresentationSpec#getMaxSize() max size} of the presentation * spec returned by {@link InlineSuggestionInfo#getPresentationSpec()}. * - * @param context Context in which to inflate the view. - * @param size The size at which to inflate the suggestion. - * @param callback Callback for receiving the inflated view. + * <p> The caller can attach an {@link View.OnClickListener} and/or an + * {@link View.OnLongClickListener} to the view in the {@code callback} to receive click and + * long click events on the view. * + * @param context Context in which to inflate the view. + * @param size The size at which to inflate the suggestion. + * @param callback Callback for receiving the inflated view. * @throws IllegalArgumentException If an invalid argument is passed. - * @throws IllegalStateException if this method is already called. + * @throws IllegalStateException If this method is already called. */ public void inflate(@NonNull Context context, @NonNull Size size, @NonNull @CallbackExecutor Executor callbackExecutor, @@ -151,12 +154,31 @@ public final class InlineSuggestion implements Parcelable { } @Override + @BinderThread public void onContent(SurfaceControlViewHost.SurfacePackage content) { final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get(); if (callbackImpl != null) { callbackImpl.onContent(content); } } + + @Override + @BinderThread + public void onClick() { + final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get(); + if (callbackImpl != null) { + callbackImpl.onClick(); + } + } + + @Override + @BinderThread + public void onLongClick() { + final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get(); + if (callbackImpl != null) { + callbackImpl.onLongClick(); + } + } } private static final class InlineContentCallbackImpl { @@ -164,6 +186,7 @@ public final class InlineSuggestion implements Parcelable { private final @NonNull Context mContext; private final @NonNull Executor mCallbackExecutor; private final @NonNull Consumer<View> mCallback; + private @Nullable View mView; InlineContentCallbackImpl(@NonNull Context context, @NonNull @CallbackExecutor Executor callbackExecutor, @@ -173,12 +196,27 @@ public final class InlineSuggestion implements Parcelable { mCallback = callback; } + @BinderThread public void onContent(SurfaceControlViewHost.SurfacePackage content) { if (content == null) { mCallbackExecutor.execute(() -> mCallback.accept(/* view */null)); } else { - mCallbackExecutor.execute( - () -> mCallback.accept(new InlineContentView(mContext, content))); + mView = new InlineContentView(mContext, content); + mCallbackExecutor.execute(() -> mCallback.accept(mView)); + } + } + + @BinderThread + public void onClick() { + if (mView != null && mView.hasOnClickListeners()) { + mView.callOnClick(); + } + } + + @BinderThread + public void onLongClick() { + if (mView != null && mView.hasOnLongClickListeners()) { + mView.performLongClick(); } } } @@ -201,7 +239,7 @@ public final class InlineSuggestion implements Parcelable { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -360,8 +398,8 @@ public final class InlineSuggestion implements Parcelable { }; @DataClass.Generated( - time = 1581929285156L, - codegenVersion = "1.0.14", + time = 1583889058241L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java", inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)") @Deprecated diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index e50da40cdc16..8e8c7dffd199 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -44,15 +44,15 @@ public final class InlineSuggestionsRequest implements Parcelable { public static final int SUGGESTION_COUNT_UNLIMITED = Integer.MAX_VALUE; /** - * Max number of suggestions expected from the response. Defaults to {@code - * SUGGESTION_COUNT_UNLIMITED} if not set. + * Max number of suggestions expected from the response. It must be a positive value. + * Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set. */ private final int mMaxSuggestionCount; /** * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion * count is larger than the number of specs in the list, then the last spec is used for the - * remainder of the suggestions. + * remainder of the suggestions. The list should not be empty. */ private final @NonNull List<InlinePresentationSpec> mPresentationSpecs; @@ -72,6 +72,7 @@ public final class InlineSuggestionsRequest implements Parcelable { /** * The extras state propagated from the IME to pass extra data. */ + @DataClass.MaySetToNull private @Nullable Bundle mExtras; /** @@ -80,6 +81,7 @@ public final class InlineSuggestionsRequest implements Parcelable { * * @hide */ + @DataClass.MaySetToNull private @Nullable IBinder mHostInputToken; /** @@ -117,6 +119,7 @@ public final class InlineSuggestionsRequest implements Parcelable { } private void onConstructed() { + Preconditions.checkState(!mPresentationSpecs.isEmpty()); Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size()); } @@ -162,7 +165,7 @@ public final class InlineSuggestionsRequest implements Parcelable { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -202,8 +205,8 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** - * Max number of suggestions expected from the response. Defaults to {@code - * SUGGESTION_COUNT_UNLIMITED} if not set. + * Max number of suggestions expected from the response. It must be a positive value. + * Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set. */ @DataClass.Generated.Member public int getMaxSuggestionCount() { @@ -213,7 +216,7 @@ public final class InlineSuggestionsRequest implements Parcelable { /** * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion * count is larger than the number of specs in the list, then the last spec is used for the - * remainder of the suggestions. + * remainder of the suggestions. The list should not be empty. */ @DataClass.Generated.Member public @NonNull List<InlinePresentationSpec> getPresentationSpecs() { @@ -419,7 +422,7 @@ public final class InlineSuggestionsRequest implements Parcelable { * @param presentationSpecs * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion * count is larger than the number of specs in the list, then the last spec is used for the - * remainder of the suggestions. + * remainder of the suggestions. The list should not be empty. */ public Builder( @NonNull List<InlinePresentationSpec> presentationSpecs) { @@ -429,8 +432,8 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** - * Max number of suggestions expected from the response. Defaults to {@code - * SUGGESTION_COUNT_UNLIMITED} if not set. + * Max number of suggestions expected from the response. It must be a positive value. + * Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set. */ @DataClass.Generated.Member public @NonNull Builder setMaxSuggestionCount(int value) { @@ -443,7 +446,7 @@ public final class InlineSuggestionsRequest implements Parcelable { /** * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion * count is larger than the number of specs in the list, then the last spec is used for the - * remainder of the suggestions. + * remainder of the suggestions. The list should not be empty. */ @DataClass.Generated.Member @Override @@ -575,10 +578,10 @@ public final class InlineSuggestionsRequest implements Parcelable { } @DataClass.Generated( - time = 1582339908980L, - codegenVersion = "1.0.14", + time = 1583975428858L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java", - inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @com.android.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.os.Bundle mExtras\nprivate @com.android.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index 6246b507ba59..842ba2975b3b 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -21,15 +21,12 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; -import android.annotation.UserIdInt; import android.app.Person; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.os.UserHandle; import android.text.SpannedString; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; @@ -317,13 +314,9 @@ public final class ConversationActions implements Parcelable { @NonNull @Hint private final List<String> mHints; - @Nullable - private String mCallingPackageName; - @UserIdInt - private int mUserId = UserHandle.USER_NULL; @NonNull private Bundle mExtras; - private boolean mUseDefaultTextClassifier; + @Nullable private SystemTextClassifierMetadata mSystemTcMetadata; private Request( @NonNull List<Message> conversation, @@ -345,10 +338,8 @@ public final class ConversationActions implements Parcelable { int maxSuggestions = in.readInt(); List<String> hints = new ArrayList<>(); in.readStringList(hints); - String callingPackageName = in.readString(); - int userId = in.readInt(); Bundle extras = in.readBundle(); - boolean useDefaultTextClassifier = in.readBoolean(); + SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); Request request = new Request( conversation, @@ -356,9 +347,7 @@ public final class ConversationActions implements Parcelable { maxSuggestions, hints, extras); - request.setCallingPackageName(callingPackageName); - request.setUserId(userId); - request.setUseDefaultTextClassifier(useDefaultTextClassifier); + request.setSystemTextClassifierMetadata(systemTcMetadata); return request; } @@ -368,10 +357,8 @@ public final class ConversationActions implements Parcelable { parcel.writeParcelable(mTypeConfig, flags); parcel.writeInt(mMaxSuggestions); parcel.writeStringList(mHints); - parcel.writeString(mCallingPackageName); - parcel.writeInt(mUserId); parcel.writeBundle(mExtras); - parcel.writeBoolean(mUseDefaultTextClassifier); + parcel.writeParcelable(mSystemTcMetadata, flags); } @Override @@ -421,62 +408,31 @@ public final class ConversationActions implements Parcelable { } /** - * Sets the name of the package that is sending this request. - * <p> - * Package-private for SystemTextClassifier's use. - * @hide - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public void setCallingPackageName(@Nullable String callingPackageName) { - mCallingPackageName = callingPackageName; - } - - /** * Returns the name of the package that sent this request. * This returns {@code null} if no calling package name is set. */ @Nullable public String getCallingPackageName() { - return mCallingPackageName; + return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null; } /** - * Sets the id of the user that sent this request. - * <p> - * Package-private for SystemTextClassifier's use. - * @hide - */ - void setUserId(@UserIdInt int userId) { - mUserId = userId; - } - - /** - * Returns the id of the user that sent this request. - * @hide - */ - @UserIdInt - public int getUserId() { - return mUserId; - } - - /** - * Sets whether to use the default text classifier to handle this request. - * This will be ignored if it is not the system text classifier to handle this request. + * Sets the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { - mUseDefaultTextClassifier = useDefaultTextClassifier; + void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcData) { + mSystemTcMetadata = systemTcData; } /** - * Returns whether to use the default text classifier to handle this request. This - * will be ignored if it is not the system text classifier to handle this request. + * Returns the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - public boolean getUseDefaultTextClassifier() { - return mUseDefaultTextClassifier; + @Nullable + public SystemTextClassifierMetadata getSystemTextClassifierMetadata() { + return mSystemTcMetadata; } /** diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java index e0f29a981d89..9a5454472076 100644 --- a/core/java/android/view/textclassifier/SelectionEvent.java +++ b/core/java/android/view/textclassifier/SelectionEvent.java @@ -19,10 +19,8 @@ package android.view.textclassifier; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.os.Parcel; import android.os.Parcelable; -import android.os.UserHandle; import android.view.textclassifier.TextClassifier.EntityType; import android.view.textclassifier.TextClassifier.WidgetType; @@ -129,7 +127,6 @@ public final class SelectionEvent implements Parcelable { private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN; private @InvocationMethod int mInvocationMethod; @Nullable private String mWidgetVersion; - private @UserIdInt int mUserId = UserHandle.USER_NULL; @Nullable private String mResultId; private long mEventTime; private long mDurationSinceSessionStart; @@ -140,7 +137,7 @@ public final class SelectionEvent implements Parcelable { private int mEnd; private int mSmartStart; private int mSmartEnd; - private boolean mUseDefaultTextClassifier; + @Nullable private SystemTextClassifierMetadata mSystemTcMetadata; SelectionEvent( int start, int end, @@ -161,6 +158,7 @@ public final class SelectionEvent implements Parcelable { mEventType = in.readInt(); mEntityType = in.readString(); mWidgetVersion = in.readInt() > 0 ? in.readString() : null; + // TODO: remove mPackageName once aiai does not need it mPackageName = in.readString(); mWidgetType = in.readString(); mInvocationMethod = in.readInt(); @@ -175,8 +173,7 @@ public final class SelectionEvent implements Parcelable { mEnd = in.readInt(); mSmartStart = in.readInt(); mSmartEnd = in.readInt(); - mUserId = in.readInt(); - mUseDefaultTextClassifier = in.readBoolean(); + mSystemTcMetadata = in.readParcelable(null); } @Override @@ -189,6 +186,7 @@ public final class SelectionEvent implements Parcelable { if (mWidgetVersion != null) { dest.writeString(mWidgetVersion); } + // TODO: remove mPackageName once aiai does not need it dest.writeString(mPackageName); dest.writeString(mWidgetType); dest.writeInt(mInvocationMethod); @@ -205,8 +203,7 @@ public final class SelectionEvent implements Parcelable { dest.writeInt(mEnd); dest.writeInt(mSmartStart); dest.writeInt(mSmartEnd); - dest.writeInt(mUserId); - dest.writeBoolean(mUseDefaultTextClassifier); + dest.writeParcelable(mSystemTcMetadata, flags); } @Override @@ -409,45 +406,26 @@ public final class SelectionEvent implements Parcelable { */ @NonNull public String getPackageName() { - return mPackageName; + return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : ""; } /** - * Sets the id of this event's user. - * <p> - * Package-private for SystemTextClassifier's use. - */ - void setUserId(@UserIdInt int userId) { - mUserId = userId; - } - - /** - * Returns the id of this event's user. - * @hide - */ - @UserIdInt - public int getUserId() { - return mUserId; - } - - /** - * Sets whether to use the default text classifier to handle this request. - * This will be ignored if it is not the system text classifier to handle this request. + * Sets the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { - mUseDefaultTextClassifier = useDefaultTextClassifier; + void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcMetadata) { + mSystemTcMetadata = systemTcMetadata; } /** - * Returns whether to use the default text classifier to handle this request. This - * will be ignored if it is not the system text classifier to handle this request. + * Returns the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - public boolean getUseDefaultTextClassifier() { - return mUseDefaultTextClassifier; + @Nullable + public SystemTextClassifierMetadata getSystemTextClassifierMetadata() { + return mSystemTcMetadata; } /** @@ -476,7 +454,7 @@ public final class SelectionEvent implements Parcelable { mPackageName = context.getPackageName(); mWidgetType = context.getWidgetType(); mWidgetVersion = context.getWidgetVersion(); - mUserId = context.getUserId(); + mSystemTcMetadata = context.getSystemTextClassifierMetadata(); } /** @@ -663,10 +641,9 @@ public final class SelectionEvent implements Parcelable { @Override public int hashCode() { return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType, - mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId, + mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent, - mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, - mUseDefaultTextClassifier); + mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mSystemTcMetadata); } @Override @@ -685,7 +662,6 @@ public final class SelectionEvent implements Parcelable { && Objects.equals(mEntityType, other.mEntityType) && Objects.equals(mWidgetVersion, other.mWidgetVersion) && Objects.equals(mPackageName, other.mPackageName) - && mUserId == other.mUserId && Objects.equals(mWidgetType, other.mWidgetType) && mInvocationMethod == other.mInvocationMethod && Objects.equals(mResultId, other.mResultId) @@ -698,7 +674,7 @@ public final class SelectionEvent implements Parcelable { && mEnd == other.mEnd && mSmartStart == other.mSmartStart && mSmartEnd == other.mSmartEnd - && mUseDefaultTextClassifier == other.mUseDefaultTextClassifier; + && mSystemTcMetadata == other.mSystemTcMetadata; } @Override @@ -706,15 +682,14 @@ public final class SelectionEvent implements Parcelable { return String.format(Locale.US, "SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, " + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, " - + "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, " + + "resultId=%s, eventTime=%d, durationSinceSessionStart=%d, " + "durationSincePreviousEvent=%d, eventIndex=%d," + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d, " - + "mUseDefaultTextClassifier=%b}", + + "systemTcMetadata=%s}", mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType, mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, - mUserId, mResultId, mEventTime, mDurationSinceSessionStart, - mDurationSincePreviousEvent, mEventIndex, - mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mUseDefaultTextClassifier); + mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent, + mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mSystemTcMetadata); } public static final @android.annotation.NonNull Creator<SelectionEvent> CREATOR = new Creator<SelectionEvent>() { diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java index fe5e8d658dc3..86ef4e103990 100644 --- a/core/java/android/view/textclassifier/SystemTextClassifier.java +++ b/core/java/android/view/textclassifier/SystemTextClassifier.java @@ -18,7 +18,6 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.content.Context; import android.os.Bundle; @@ -39,7 +38,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** - * Proxy to the system's default TextClassifier. + * proxy to the request to TextClassifierService via the TextClassificationManagerService. + * * @hide */ @VisibleForTesting(visibility = Visibility.PACKAGE) @@ -50,14 +50,19 @@ public final class SystemTextClassifier implements TextClassifier { private final ITextClassifierService mManagerService; private final TextClassificationConstants mSettings; private final TextClassifier mFallback; - private final String mPackageName; - // NOTE: Always set this before sending a request to the manager service otherwise the manager - // service will throw a remote exception. - @UserIdInt - private final int mUserId; - private final boolean mUseDefault; private TextClassificationSessionId mSessionId; + // NOTE: Always set this before sending a request to the manager service otherwise the + // manager service will throw a remote exception. + @NonNull + private final SystemTextClassifierMetadata mSystemTcMetadata; + /** + * Constructor of {@link SystemTextClassifier} + * + * @param context the context of the request. + * @param settings TextClassifier specific settings. + * @param useDefault whether to use the default text classifier to handle this request + */ public SystemTextClassifier( Context context, TextClassificationConstants settings, @@ -66,9 +71,11 @@ public final class SystemTextClassifier implements TextClassifier { ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE)); mSettings = Objects.requireNonNull(settings); mFallback = TextClassifier.NO_OP; - mPackageName = Objects.requireNonNull(context.getOpPackageName()); - mUserId = context.getUserId(); - mUseDefault = useDefault; + // NOTE: Always set this before sending a request to the manager service otherwise the + // manager service will throw a remote exception. + mSystemTcMetadata = new SystemTextClassifierMetadata( + Objects.requireNonNull(context.getOpPackageName()), context.getUserId(), + useDefault); } /** @@ -80,9 +87,7 @@ public final class SystemTextClassifier implements TextClassifier { Objects.requireNonNull(request); Utils.checkMainThread(); try { - request.setCallingPackageName(mPackageName); - request.setUserId(mUserId); - request.setUseDefaultTextClassifier(mUseDefault); + request.setSystemTextClassifierMetadata(mSystemTcMetadata); final BlockingCallback<TextSelection> callback = new BlockingCallback<>("textselection"); mManagerService.onSuggestSelection(mSessionId, request, callback); @@ -105,9 +110,7 @@ public final class SystemTextClassifier implements TextClassifier { Objects.requireNonNull(request); Utils.checkMainThread(); try { - request.setCallingPackageName(mPackageName); - request.setUserId(mUserId); - request.setUseDefaultTextClassifier(mUseDefault); + request.setSystemTextClassifierMetadata(mSystemTcMetadata); final BlockingCallback<TextClassification> callback = new BlockingCallback<>("textclassification"); mManagerService.onClassifyText(mSessionId, request, callback); @@ -137,9 +140,7 @@ public final class SystemTextClassifier implements TextClassifier { } try { - request.setCallingPackageName(mPackageName); - request.setUserId(mUserId); - request.setUseDefaultTextClassifier(mUseDefault); + request.setSystemTextClassifierMetadata(mSystemTcMetadata); final BlockingCallback<TextLinks> callback = new BlockingCallback<>("textlinks"); mManagerService.onGenerateLinks(mSessionId, request, callback); @@ -159,8 +160,7 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { - event.setUserId(mUserId); - event.setUseDefaultTextClassifier(mUseDefault); + event.setSystemTextClassifierMetadata(mSystemTcMetadata); mManagerService.onSelectionEvent(mSessionId, event); } catch (RemoteException e) { Log.e(LOG_TAG, "Error reporting selection event.", e); @@ -173,12 +173,11 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { - final TextClassificationContext tcContext = event.getEventContext() == null - ? new TextClassificationContext.Builder(mPackageName, WIDGET_TYPE_UNKNOWN) - .build() - : event.getEventContext(); - tcContext.setUserId(mUserId); - tcContext.setUseDefaultTextClassifier(mUseDefault); + final TextClassificationContext tcContext = + event.getEventContext() == null ? new TextClassificationContext.Builder( + mSystemTcMetadata.getCallingPackageName(), WIDGET_TYPE_UNKNOWN).build() + : event.getEventContext(); + tcContext.setSystemTextClassifierMetadata(mSystemTcMetadata); event.setEventContext(tcContext); mManagerService.onTextClassifierEvent(mSessionId, event); } catch (RemoteException e) { @@ -192,9 +191,7 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { - request.setCallingPackageName(mPackageName); - request.setUserId(mUserId); - request.setUseDefaultTextClassifier(mUseDefault); + request.setSystemTextClassifierMetadata(mSystemTcMetadata); final BlockingCallback<TextLanguage> callback = new BlockingCallback<>("textlanguage"); mManagerService.onDetectLanguage(mSessionId, request, callback); @@ -214,9 +211,7 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { - request.setCallingPackageName(mPackageName); - request.setUserId(mUserId); - request.setUseDefaultTextClassifier(mUseDefault); + request.setSystemTextClassifierMetadata(mSystemTcMetadata); final BlockingCallback<ConversationActions> callback = new BlockingCallback<>("conversation-actions"); mManagerService.onSuggestConversationActions(mSessionId, request, callback); @@ -256,10 +251,8 @@ public final class SystemTextClassifier implements TextClassifier { printWriter.println("SystemTextClassifier:"); printWriter.increaseIndent(); printWriter.printPair("mFallback", mFallback); - printWriter.printPair("mPackageName", mPackageName); printWriter.printPair("mSessionId", mSessionId); - printWriter.printPair("mUserId", mUserId); - printWriter.printPair("mUseDefault", mUseDefault); + printWriter.printPair("mSystemTcMetadata", mSystemTcMetadata); printWriter.decreaseIndent(); printWriter.println(); } @@ -275,7 +268,7 @@ public final class SystemTextClassifier implements TextClassifier { @NonNull TextClassificationSessionId sessionId) { mSessionId = Objects.requireNonNull(sessionId); try { - classificationContext.setUserId(mUserId); + classificationContext.setSystemTextClassifierMetadata(mSystemTcMetadata); mManagerService.onCreateTextClassificationSession(classificationContext, mSessionId); } catch (RemoteException e) { Log.e(LOG_TAG, "Error starting a new classification session.", e); diff --git a/core/java/android/view/textclassifier/SystemTextClassifierMetadata.aidl b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.aidl new file mode 100644 index 000000000000..4d4e90a4f346 --- /dev/null +++ b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.textclassifier; + +parcelable SystemTextClassifierMetadata;
\ No newline at end of file diff --git a/core/java/android/view/textclassifier/SystemTextClassifierMetadata.java b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.java new file mode 100644 index 000000000000..971e3e25c975 --- /dev/null +++ b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.textclassifier; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; + +import java.util.Locale; +import java.util.Objects; + +/** + * SystemTextClassifier specific information. + * <p> + * This contains information requires for the TextClassificationManagerService to process the + * requests from the application, e.g. user id, calling package name and etc. Centrialize the data + * into this class helps to extend the scalability if we want to add new fields. + * @hide + */ +@VisibleForTesting(visibility = Visibility.PACKAGE) +public final class SystemTextClassifierMetadata implements Parcelable { + + /* The name of the package that sent the TC request. */ + @NonNull + private final String mCallingPackageName; + /* The id of the user that sent the TC request. */ + @UserIdInt + private final int mUserId; + /* Whether to use the default text classifier to handle the request. */ + private final boolean mUseDefaultTextClassifier; + + public SystemTextClassifierMetadata(@NonNull String packageName, @UserIdInt int userId, + boolean useDefaultTextClassifier) { + Objects.requireNonNull(packageName); + mCallingPackageName = packageName; + mUserId = userId; + mUseDefaultTextClassifier = useDefaultTextClassifier; + } + + /** + * Returns the id of the user that sent the TC request. + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** + * Returns the name of the package that sent the TC request. + * This returns {@code null} if no calling package name is set. + */ + @NonNull + public String getCallingPackageName() { + return mCallingPackageName; + } + + /** + * Returns whether to use the default text classifier to handle TC request. + */ + public boolean useDefaultTextClassifier() { + return mUseDefaultTextClassifier; + } + + @Override + public String toString() { + return String.format(Locale.US, + "SystemTextClassifierMetadata {callingPackageName=%s, userId=%d, " + + "useDefaultTextClassifier=%b}", + mCallingPackageName, mUserId, mUseDefaultTextClassifier); + } + + private static SystemTextClassifierMetadata readFromParcel(Parcel in) { + final String packageName = in.readString(); + final int userId = in.readInt(); + final boolean useDefaultTextClassifier = in.readBoolean(); + return new SystemTextClassifierMetadata(packageName, userId, useDefaultTextClassifier); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mCallingPackageName); + dest.writeInt(mUserId); + dest.writeBoolean(mUseDefaultTextClassifier); + } + + public static final @NonNull Creator<SystemTextClassifierMetadata> CREATOR = + new Creator<SystemTextClassifierMetadata>() { + @Override + public SystemTextClassifierMetadata createFromParcel(Parcel in) { + return readFromParcel(in); + } + + @Override + public SystemTextClassifierMetadata[] newArray(int size) { + return new SystemTextClassifierMetadata[size]; + } + }; +} diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 00f762b44a07..8b9d12916595 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -21,7 +21,6 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.app.PendingIntent; import android.app.RemoteAction; import android.content.Context; @@ -36,7 +35,6 @@ import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; -import android.os.UserHandle; import android.text.SpannedString; import android.util.ArrayMap; import android.view.View.OnClickListener; @@ -552,10 +550,7 @@ public final class TextClassification implements Parcelable { @Nullable private final LocaleList mDefaultLocales; @Nullable private final ZonedDateTime mReferenceTime; @NonNull private final Bundle mExtras; - @Nullable private String mCallingPackageName; - @UserIdInt - private int mUserId = UserHandle.USER_NULL; - private boolean mUseDefaultTextClassifier; + @Nullable private SystemTextClassifierMetadata mSystemTcMetadata; private Request( CharSequence text, @@ -616,62 +611,33 @@ public final class TextClassification implements Parcelable { } /** - * Sets the name of the package that is sending this request. - * <p> - * For SystemTextClassifier's use. - * @hide - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public void setCallingPackageName(@Nullable String callingPackageName) { - mCallingPackageName = callingPackageName; - } - - /** * Returns the name of the package that sent this request. * This returns {@code null} if no calling package name is set. */ @Nullable public String getCallingPackageName() { - return mCallingPackageName; - } - - /** - * Sets the id of the user that sent this request. - * <p> - * Package-private for SystemTextClassifier's use. - * @hide - */ - void setUserId(@UserIdInt int userId) { - mUserId = userId; + return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null; } /** - * Returns the id of the user that sent this request. - * @hide - */ - @UserIdInt - public int getUserId() { - return mUserId; - } - - /** - * Sets whether to use the default text classifier to handle this request. - * This will be ignored if it is not the system text classifier to handle this request. + * Sets the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { - mUseDefaultTextClassifier = useDefaultTextClassifier; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public void setSystemTextClassifierMetadata( + @Nullable SystemTextClassifierMetadata systemTcMetadata) { + mSystemTcMetadata = systemTcMetadata; } /** - * Returns whether to use the default text classifier to handle this request. This - * will be ignored if it is not the system text classifier to handle this request. + * Returns the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - public boolean getUseDefaultTextClassifier() { - return mUseDefaultTextClassifier; + @Nullable + public SystemTextClassifierMetadata getSystemTextClassifierMetadata() { + return mSystemTcMetadata; } /** @@ -773,10 +739,8 @@ public final class TextClassification implements Parcelable { dest.writeInt(mEndIndex); dest.writeParcelable(mDefaultLocales, flags); dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString()); - dest.writeString(mCallingPackageName); - dest.writeInt(mUserId); dest.writeBundle(mExtras); - dest.writeBoolean(mUseDefaultTextClassifier); + dest.writeParcelable(mSystemTcMetadata, flags); } private static Request readFromParcel(Parcel in) { @@ -787,16 +751,12 @@ public final class TextClassification implements Parcelable { final String referenceTimeString = in.readString(); final ZonedDateTime referenceTime = referenceTimeString == null ? null : ZonedDateTime.parse(referenceTimeString); - final String callingPackageName = in.readString(); - final int userId = in.readInt(); final Bundle extras = in.readBundle(); - final boolean useDefaultTextClassifier = in.readBoolean(); + final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); final Request request = new Request(text, startIndex, endIndex, defaultLocales, referenceTime, extras); - request.setCallingPackageName(callingPackageName); - request.setUserId(userId); - request.setUseDefaultTextClassifier(useDefaultTextClassifier); + request.setSystemTextClassifierMetadata(systemTcMetadata); return request; } diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java index d58d175c9c93..f2323c625d15 100644 --- a/core/java/android/view/textclassifier/TextClassificationContext.java +++ b/core/java/android/view/textclassifier/TextClassificationContext.java @@ -18,10 +18,8 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.os.Parcel; import android.os.Parcelable; -import android.os.UserHandle; import android.view.textclassifier.TextClassifier.WidgetType; import java.util.Locale; @@ -33,12 +31,11 @@ import java.util.Objects; */ public final class TextClassificationContext implements Parcelable { - private final String mPackageName; + // NOTE: Modify packageName only in the constructor or in setSystemTextClassifierMetadata() + private String mPackageName; private final String mWidgetType; @Nullable private final String mWidgetVersion; - @UserIdInt - private int mUserId = UserHandle.USER_NULL; - private boolean mUseDefaultTextClassifier; + private SystemTextClassifierMetadata mSystemTcMetadata; private TextClassificationContext( String packageName, @@ -58,42 +55,26 @@ public final class TextClassificationContext implements Parcelable { } /** - * Sets the id of this context's user. - * <p> - * Package-private for SystemTextClassifier's use. - * @hide - */ - void setUserId(@UserIdInt int userId) { - mUserId = userId; - } - - /** - * Returns the id of this context's user. - * @hide - */ - @UserIdInt - public int getUserId() { - return mUserId; - } - - /** - * Sets whether to use the default text classifier to handle this request. - * This will be ignored if it is not the system text classifier to handle this request. + * Sets the information about the {@link SystemTextClassifier} that sent this request. * + * <p><b>NOTE: </b>This will override the value returned in {@link getPackageName()}. * @hide */ - void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { - mUseDefaultTextClassifier = useDefaultTextClassifier; + void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcMetadata) { + mSystemTcMetadata = systemTcMetadata; + if (mSystemTcMetadata != null) { + mPackageName = mSystemTcMetadata.getCallingPackageName(); + } } /** - * Returns whether to use the default text classifier to handle this request. This - * will be ignored if it is not the system text classifier to handle this request. + * Returns the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - public boolean getUseDefaultTextClassifier() { - return mUseDefaultTextClassifier; + @Nullable + public SystemTextClassifierMetadata getSystemTextClassifierMetadata() { + return mSystemTcMetadata; } /** @@ -118,8 +99,8 @@ public final class TextClassificationContext implements Parcelable { @Override public String toString() { return String.format(Locale.US, "TextClassificationContext{" - + "packageName=%s, widgetType=%s, widgetVersion=%s, userId=%d}", - mPackageName, mWidgetType, mWidgetVersion, mUserId); + + "packageName=%s, widgetType=%s, widgetVersion=%s, systemTcMetadata=%s}", + mPackageName, mWidgetType, mWidgetVersion, mSystemTcMetadata); } /** @@ -176,16 +157,14 @@ public final class TextClassificationContext implements Parcelable { parcel.writeString(mPackageName); parcel.writeString(mWidgetType); parcel.writeString(mWidgetVersion); - parcel.writeInt(mUserId); - parcel.writeBoolean(mUseDefaultTextClassifier); + parcel.writeParcelable(mSystemTcMetadata, flags); } private TextClassificationContext(Parcel in) { mPackageName = in.readString(); mWidgetType = in.readString(); mWidgetVersion = in.readString(); - mUserId = in.readInt(); - mUseDefaultTextClassifier = in.readBoolean(); + mSystemTcMetadata = in.readParcelable(null); } public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR = diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java index 58024dcc09b9..1e8253db888a 100644 --- a/core/java/android/view/textclassifier/TextLanguage.java +++ b/core/java/android/view/textclassifier/TextLanguage.java @@ -20,12 +20,10 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.icu.util.ULocale; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.os.UserHandle; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; @@ -227,10 +225,7 @@ public final class TextLanguage implements Parcelable { private final CharSequence mText; private final Bundle mExtra; - @Nullable private String mCallingPackageName; - @UserIdInt - private int mUserId = UserHandle.USER_NULL; - private boolean mUseDefaultTextClassifier; + @Nullable private SystemTextClassifierMetadata mSystemTcMetadata; private Request(CharSequence text, Bundle bundle) { mText = text; @@ -246,61 +241,33 @@ public final class TextLanguage implements Parcelable { } /** - * Sets the name of the package that is sending this request. - * Package-private for SystemTextClassifier's use. - * @hide - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public void setCallingPackageName(@Nullable String callingPackageName) { - mCallingPackageName = callingPackageName; - } - - /** * Returns the name of the package that sent this request. * This returns null if no calling package name is set. */ @Nullable public String getCallingPackageName() { - return mCallingPackageName; + return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null; } /** - * Sets the id of the user that sent this request. - * <p> - * Package-private for SystemTextClassifier's use. - * @hide - */ - void setUserId(@UserIdInt int userId) { - mUserId = userId; - } - - /** - * Returns the id of the user that sent this request. - * @hide - */ - @UserIdInt - public int getUserId() { - return mUserId; - } - - /** - * Sets whether to use the default text classifier to handle this request. - * This will be ignored if it is not the system text classifier to handle this request. + * Sets the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { - mUseDefaultTextClassifier = useDefaultTextClassifier; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public void setSystemTextClassifierMetadata( + @Nullable SystemTextClassifierMetadata systemTcMetadata) { + mSystemTcMetadata = systemTcMetadata; } /** - * Returns whether to use the default text classifier to handle this request. This - * will be ignored if it is not the system text classifier to handle this request. + * Returns the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - public boolean getUseDefaultTextClassifier() { - return mUseDefaultTextClassifier; + @Nullable + public SystemTextClassifierMetadata getSystemTextClassifierMetadata() { + return mSystemTcMetadata; } /** @@ -321,23 +288,17 @@ public final class TextLanguage implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeCharSequence(mText); - dest.writeString(mCallingPackageName); - dest.writeInt(mUserId); dest.writeBundle(mExtra); - dest.writeBoolean(mUseDefaultTextClassifier); + dest.writeParcelable(mSystemTcMetadata, flags); } private static Request readFromParcel(Parcel in) { final CharSequence text = in.readCharSequence(); - final String callingPackageName = in.readString(); - final int userId = in.readInt(); final Bundle extra = in.readBundle(); - final boolean useDefaultTextClassifier = in.readBoolean(); + final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); final Request request = new Request(text, extra); - request.setCallingPackageName(callingPackageName); - request.setUserId(userId); - request.setUseDefaultTextClassifier(useDefaultTextClassifier); + request.setSystemTextClassifierMetadata(systemTcMetadata); return request; } diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index 7430cb38b987..dea3a9010b18 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -20,13 +20,11 @@ import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.content.Context; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; -import android.os.UserHandle; import android.text.Spannable; import android.text.method.MovementMethod; import android.text.style.ClickableSpan; @@ -340,12 +338,9 @@ public final class TextLinks implements Parcelable { @Nullable private final LocaleList mDefaultLocales; @Nullable private final EntityConfig mEntityConfig; private final boolean mLegacyFallback; - @Nullable private String mCallingPackageName; private final Bundle mExtras; @Nullable private final ZonedDateTime mReferenceTime; - @UserIdInt - private int mUserId = UserHandle.USER_NULL; - private boolean mUseDefaultTextClassifier; + @Nullable private SystemTextClassifierMetadata mSystemTcMetadata; private Request( CharSequence text, @@ -409,62 +404,33 @@ public final class TextLinks implements Parcelable { } /** - * Sets the name of the package that is sending this request. - * <p> - * Package-private for SystemTextClassifier's use. - * @hide - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public void setCallingPackageName(@Nullable String callingPackageName) { - mCallingPackageName = callingPackageName; - } - - /** * Returns the name of the package that sent this request. * This returns {@code null} if no calling package name is set. */ @Nullable public String getCallingPackageName() { - return mCallingPackageName; - } - - /** - * Sets the id of the user that sent this request. - * <p> - * Package-private for SystemTextClassifier's use. - * @hide - */ - void setUserId(@UserIdInt int userId) { - mUserId = userId; + return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null; } /** - * Returns the id of the user that sent this request. - * @hide - */ - @UserIdInt - public int getUserId() { - return mUserId; - } - - /** - * Sets whether to use the default text classifier to handle this request. - * This will be ignored if it is not the system text classifier to handle this request. + * Sets the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { - mUseDefaultTextClassifier = useDefaultTextClassifier; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public void setSystemTextClassifierMetadata( + @Nullable SystemTextClassifierMetadata systemTcMetadata) { + mSystemTcMetadata = systemTcMetadata; } /** - * Returns whether to use the default text classifier to handle this request. This - * will be ignored if it is not the system text classifier to handle this request. + * Returns the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - public boolean getUseDefaultTextClassifier() { - return mUseDefaultTextClassifier; + @Nullable + public SystemTextClassifierMetadata getSystemTextClassifierMetadata() { + return mSystemTcMetadata; } /** @@ -585,30 +551,24 @@ public final class TextLinks implements Parcelable { dest.writeString(mText.toString()); dest.writeParcelable(mDefaultLocales, flags); dest.writeParcelable(mEntityConfig, flags); - dest.writeString(mCallingPackageName); - dest.writeInt(mUserId); dest.writeBundle(mExtras); dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString()); - dest.writeBoolean(mUseDefaultTextClassifier); + dest.writeParcelable(mSystemTcMetadata, flags); } private static Request readFromParcel(Parcel in) { final String text = in.readString(); final LocaleList defaultLocales = in.readParcelable(null); final EntityConfig entityConfig = in.readParcelable(null); - final String callingPackageName = in.readString(); - final int userId = in.readInt(); final Bundle extras = in.readBundle(); final String referenceTimeString = in.readString(); final ZonedDateTime referenceTime = referenceTimeString == null ? null : ZonedDateTime.parse(referenceTimeString); - final boolean useDefaultTextClassifier = in.readBoolean(); + final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); final Request request = new Request(text, defaultLocales, entityConfig, /* legacyFallback= */ true, referenceTime, extras); - request.setCallingPackageName(callingPackageName); - request.setUserId(userId); - request.setUseDefaultTextClassifier(useDefaultTextClassifier); + request.setSystemTextClassifierMetadata(systemTcMetadata); return request; } diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index 575a072d7066..d8a632d10bc3 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -20,12 +20,10 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; -import android.os.UserHandle; import android.text.SpannedString; import android.util.ArrayMap; import android.view.textclassifier.TextClassifier.EntityType; @@ -213,10 +211,7 @@ public final class TextSelection implements Parcelable { @Nullable private final LocaleList mDefaultLocales; private final boolean mDarkLaunchAllowed; private final Bundle mExtras; - @Nullable private String mCallingPackageName; - @UserIdInt - private int mUserId = UserHandle.USER_NULL; - private boolean mUseDefaultTextClassifier; + @Nullable private SystemTextClassifierMetadata mSystemTcMetadata; private Request( CharSequence text, @@ -278,62 +273,33 @@ public final class TextSelection implements Parcelable { } /** - * Sets the name of the package that is sending this request. - * <p> - * Package-private for SystemTextClassifier's use. - * @hide - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public void setCallingPackageName(@Nullable String callingPackageName) { - mCallingPackageName = callingPackageName; - } - - /** * Returns the name of the package that sent this request. * This returns {@code null} if no calling package name is set. */ @Nullable public String getCallingPackageName() { - return mCallingPackageName; - } - - /** - * Sets the id of the user that sent this request. - * <p> - * Package-private for SystemTextClassifier's use. - * @hide - */ - void setUserId(@UserIdInt int userId) { - mUserId = userId; + return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null; } /** - * Returns the id of the user that sent this request. - * @hide - */ - @UserIdInt - public int getUserId() { - return mUserId; - } - - /** - * Sets whether to use the default text classifier to handle this request. - * This will be ignored if it is not the system text classifier to handle this request. + * Sets the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { - mUseDefaultTextClassifier = useDefaultTextClassifier; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public void setSystemTextClassifierMetadata( + @Nullable SystemTextClassifierMetadata systemTcMetadata) { + mSystemTcMetadata = systemTcMetadata; } /** - * Returns whether to use the default text classifier to handle this request. This - * will be ignored if it is not the system text classifier to handle this request. + * Returns the information about the {@link SystemTextClassifier} that sent this request. * * @hide */ - public boolean getUseDefaultTextClassifier() { - return mUseDefaultTextClassifier; + @Nullable + public SystemTextClassifierMetadata getSystemTextClassifierMetadata() { + return mSystemTcMetadata; } /** @@ -438,10 +404,8 @@ public final class TextSelection implements Parcelable { dest.writeInt(mStartIndex); dest.writeInt(mEndIndex); dest.writeParcelable(mDefaultLocales, flags); - dest.writeString(mCallingPackageName); - dest.writeInt(mUserId); dest.writeBundle(mExtras); - dest.writeBoolean(mUseDefaultTextClassifier); + dest.writeParcelable(mSystemTcMetadata, flags); } private static Request readFromParcel(Parcel in) { @@ -449,16 +413,12 @@ public final class TextSelection implements Parcelable { final int startIndex = in.readInt(); final int endIndex = in.readInt(); final LocaleList defaultLocales = in.readParcelable(null); - final String callingPackageName = in.readString(); - final int userId = in.readInt(); final Bundle extras = in.readBundle(); - final boolean systemTextClassifierType = in.readBoolean(); + final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); final Request request = new Request(text, startIndex, endIndex, defaultLocales, /* darkLaunchAllowed= */ false, extras); - request.setCallingPackageName(callingPackageName); - request.setUserId(userId); - request.setUseDefaultTextClassifier(systemTextClassifierType); + request.setSystemTextClassifierMetadata(systemTcMetadata); return request; } diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index d165bd0f0fa7..ffdb89de5f59 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -261,7 +261,7 @@ public class AnalogClock extends View { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { - String tz = intent.getStringExtra("time-zone"); + String tz = intent.getStringExtra(Intent.EXTRA_TIMEZONE); mClock = Clock.system(ZoneId.of(tz)); } diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java index 85654931a975..6432438b6630 100644 --- a/core/java/android/widget/TextClock.java +++ b/core/java/android/widget/TextClock.java @@ -173,7 +173,7 @@ public class TextClock extends TextView { return; // Test disabled the clock ticks } if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) { - final String timeZone = intent.getStringExtra("time-zone"); + final String timeZone = intent.getStringExtra(Intent.EXTRA_TIMEZONE); createTime(timeZone); } else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction()) || Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 0d820654377c..2168018e12be 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -6607,6 +6607,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mTransformation instanceof PasswordTransformationMethod; } + /** + * Returns true if the current inputType is any type of password. + * + * @hide + */ + public boolean isAnyPasswordInputType() { + final int inputType = getInputType(); + return isPasswordInputType(inputType) || isVisiblePasswordInputType(inputType); + } + static boolean isPasswordInputType(int inputType) { final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION); diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index d50826f815a0..fa567f235c94 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -452,7 +452,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { mEmptyStateView = rootView.findViewById(R.id.resolver_empty_state); } - private ViewGroup getEmptyStateView() { + protected ViewGroup getEmptyStateView() { return mEmptyStateView; } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index c487e960854b..5620bff5142b 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -2398,17 +2398,22 @@ public class ChooserActivity extends ResolverActivity implements } final int availableWidth = right - left - v.getPaddingLeft() - v.getPaddingRight(); - if (mChooserMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) { - gridAdapter.calculateChooserTargetWidth(availableWidth); - return; - } - - if (gridAdapter.consumeLayoutRequest() + boolean isLayoutUpdated = gridAdapter.consumeLayoutRequest() || gridAdapter.calculateChooserTargetWidth(availableWidth) || recyclerView.getAdapter() == null - || mLastNumberOfChildren != recyclerView.getChildCount() - || availableWidth != mCurrAvailableWidth) { + || availableWidth != mCurrAvailableWidth; + if (isLayoutUpdated + || mLastNumberOfChildren != recyclerView.getChildCount()) { mCurrAvailableWidth = availableWidth; + if (isLayoutUpdated + && mChooserMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) { + // This fixes b/150936654 - empty work tab in share sheet when swiping + mChooserMultiProfilePagerAdapter.getActiveAdapterView() + .setAdapter(mChooserMultiProfilePagerAdapter.getCurrentRootAdapter()); + return; + } else if (mChooserMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) { + return; + } getMainThreadHandler().post(() -> { if (mResolverDrawerLayout == null || gridAdapter == null) { @@ -2452,39 +2457,46 @@ public class ChooserActivity extends ResolverActivity implements offset += tabDivider.getHeight(); } - int directShareHeight = 0; - rowsToShow = Math.min(4, rowsToShow); - mLastNumberOfChildren = recyclerView.getChildCount(); - for (int i = 0, childCount = recyclerView.getChildCount(); - i < childCount && rowsToShow > 0; i++) { - View child = recyclerView.getChildAt(i); - if (((GridLayoutManager.LayoutParams) - child.getLayoutParams()).getSpanIndex() != 0) { - continue; - } - int height = child.getHeight(); - offset += height; + if (recyclerView.getVisibility() == View.VISIBLE) { + int directShareHeight = 0; + rowsToShow = Math.min(4, rowsToShow); + mLastNumberOfChildren = recyclerView.getChildCount(); + for (int i = 0, childCount = recyclerView.getChildCount(); + i < childCount && rowsToShow > 0; i++) { + View child = recyclerView.getChildAt(i); + if (((GridLayoutManager.LayoutParams) + child.getLayoutParams()).getSpanIndex() != 0) { + continue; + } + int height = child.getHeight(); + offset += height; - if (gridAdapter.getTargetType( - recyclerView.getChildAdapterPosition(child)) - == ChooserListAdapter.TARGET_SERVICE) { - directShareHeight = height; + if (gridAdapter.getTargetType( + recyclerView.getChildAdapterPosition(child)) + == ChooserListAdapter.TARGET_SERVICE) { + directShareHeight = height; + } + rowsToShow--; } - rowsToShow--; - } - boolean isExpandable = getResources().getConfiguration().orientation - == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode(); - if (directShareHeight != 0 && isSendAction(getTargetIntent()) - && isExpandable) { - // make sure to leave room for direct share 4->8 expansion - int requiredExpansionHeight = - (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE); - int topInset = mSystemWindowInsets != null ? mSystemWindowInsets.top : 0; - int minHeight = bottom - top - mResolverDrawerLayout.getAlwaysShowHeight() - - requiredExpansionHeight - topInset - bottomInset; - - offset = Math.min(offset, minHeight); + boolean isExpandable = getResources().getConfiguration().orientation + == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode(); + if (directShareHeight != 0 && isSendAction(getTargetIntent()) + && isExpandable) { + // make sure to leave room for direct share 4->8 expansion + int requiredExpansionHeight = + (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE); + int topInset = mSystemWindowInsets != null ? mSystemWindowInsets.top : 0; + int minHeight = bottom - top - mResolverDrawerLayout.getAlwaysShowHeight() + - requiredExpansionHeight - topInset - bottomInset; + + offset = Math.min(offset, minHeight); + } + } else { + ViewGroup currentEmptyStateView = getCurrentEmptyStateView(); + if (currentEmptyStateView.getVisibility() == View.VISIBLE) { + offset += currentEmptyStateView.getHeight(); + } } mResolverDrawerLayout.setCollapsibleHeightReserved(Math.min(offset, bottom - top)); @@ -2492,6 +2504,11 @@ public class ChooserActivity extends ResolverActivity implements } } + private ViewGroup getCurrentEmptyStateView() { + int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage(); + return mChooserMultiProfilePagerAdapter.getItem(currentPage).getEmptyStateView(); + } + static class BaseChooserTargetComparator implements Comparator<ChooserTarget> { @Override public int compare(ChooserTarget lhs, ChooserTarget rhs) { diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 907ea55d52a0..921882348328 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -34,14 +34,14 @@ interface IAppOpsService { // be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h // and not be reordered int checkOperation(int code, int uid, String packageName); - int noteOperation(int code, int uid, String packageName, @nullable String featureId, + int noteOperation(int code, int uid, String packageName, @nullable String attributionTag, boolean shouldCollectAsyncNotedOp, String message); int startOperation(IBinder clientId, int code, int uid, String packageName, - @nullable String featureId, boolean startIfModeDefault, + @nullable String attributionTag, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message); @UnsupportedAppUsage void finishOperation(IBinder clientId, int code, int uid, String packageName, - @nullable String featureId); + @nullable String attributionTag); void startWatchingMode(int op, String packageName, IAppOpsCallback callback); void stopWatchingMode(IAppOpsCallback callback); int permissionToOpCode(String permission); @@ -52,8 +52,8 @@ interface IAppOpsService { // Any new method exposed to native must be added after the last one, do not reorder int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName, - String proxiedFeatureId, int proxyUid, String proxyPackageName, - String proxyFeatureId, boolean shouldCollectAsyncNotedOp, String message); + String proxiedAttributionTag, int proxyUid, String proxyPackageName, + String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message); // Remaining methods are only used in Java. int checkPackage(int uid, String packageName); @@ -64,10 +64,10 @@ interface IAppOpsService { List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops); @UnsupportedAppUsage List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops); - void getHistoricalOps(int uid, String packageName, String featureId, in List<String> ops, + void getHistoricalOps(int uid, String packageName, String attributionTag, in List<String> ops, int filter, long beginTimeMillis, long endTimeMillis, int flags, in RemoteCallback callback); - void getHistoricalOpsFromDiskRaw(int uid, String packageName, String featureId, + void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag, in List<String> ops, int filter, long beginTimeMillis, long endTimeMillis, int flags, in RemoteCallback callback); void offsetHistory(long duration); diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 4c203d394759..523ed6fa9a4f 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -164,6 +164,30 @@ interface IPlatformCompat boolean clearOverride(long changeId, String packageName); /** + * Enable all compatibility changes which have enabledAfterTargetSdk == + * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the + * changes to take effect. + * + * @param packageName The package name of the app whose compatibility changes will be enabled. + * @param targetSdkVersion The targetSdkVersion for filtering the changes to be enabled. + * + * @return The number of changes that were enabled. + */ + int enableTargetSdkChanges(in String packageName, int targetSdkVersion); + + /** + * Disable all compatibility changes which have enabledAfterTargetSdk == + * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the + * changes to take effect. + * + * @param packageName The package name of the app whose compatibility changes will be disabled. + * @param targetSdkVersion The targetSdkVersion for filtering the changes to be disabled. + * + * @return The number of changes that were disabled. + */ + int disableTargetSdkChanges(in String packageName, int targetSdkVersion); + + /** * Revert overrides to compatibility changes. Kills the app to allow the changes to take effect. * * @param packageName The package name of the app whose overrides will be cleared. diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index 73ef8c6f6fca..2f048c95ae4e 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -430,7 +430,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { if (shouldHide(file)) continue; if (file.isDirectory()) { - for (File child : file.listFiles()) { + for (File child : FileUtils.listFilesOrEmpty(file)) { pending.add(child); } } diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java index 91ef0b5f9d3f..1296ddc01c35 100644 --- a/core/java/com/android/internal/notification/SystemNotificationChannels.java +++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java @@ -116,6 +116,7 @@ public class SystemNotificationChannels { NETWORK_STATUS, context.getString(R.string.notification_channel_network_status), NotificationManager.IMPORTANCE_LOW); + network.setBlockableSystem(true); channelsList.add(network); final NotificationChannel networkAlertsChannel = new NotificationChannel( diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 94924a57e3f2..633aa2c5ffb4 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -140,6 +140,33 @@ public final class Zygote { */ public static final int MEMORY_TAG_LEVEL_SYNC = 3 << 19; + /** + * A two-bit field for GWP-ASan level of this process. See the possible values below. + */ + public static final int GWP_ASAN_LEVEL_MASK = (1 << 21) | (1 << 22); + + /** + * Disable GWP-ASan in this process. + * GWP-ASan is a low-overhead memory bug detector using guard pages on a small + * subset of heap allocations. + */ + public static final int GWP_ASAN_LEVEL_NEVER = 0 << 21; + + /** + * Enable GWP-ASan in this process with a small sampling rate. + * With approx. 1% chance GWP-ASan will be activated and apply its protection + * to a small subset of heap allocations. + * Otherwise (~99% chance) this process is unaffected. + */ + public static final int GWP_ASAN_LEVEL_LOTTERY = 1 << 21; + + /** + * Always enable GWP-ASan in this process. + * GWP-ASan is activated unconditionally (but still, only a small subset of + * allocations is protected). + */ + public static final int GWP_ASAN_LEVEL_ALWAYS = 2 << 21; + /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; /** Default external storage should be mounted. */ diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 54cf693490e9..0bfd65936b4a 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -125,12 +125,6 @@ public class ZygoteInit { private static boolean sPreloadComplete; - /** - * Cached classloader to use for the system server. Will only be populated in the system - * server process. - */ - private static ClassLoader sCachedSystemServerClassLoader = null; - static void preload(TimingsTraceLog bootTimingsTraceLog) { Log.d(TAG, "begin preload"); bootTimingsTraceLog.traceBegin("BeginPreload"); @@ -508,13 +502,7 @@ public class ZygoteInit { final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH"); if (systemServerClasspath != null) { - if (performSystemServerDexOpt(systemServerClasspath)) { - // Throw away the cached classloader. If we compiled here, the classloader would - // not have had AoT-ed artifacts. - // Note: This only works in a very special environment where selinux enforcement is - // disabled, e.g., Mac builds. - sCachedSystemServerClassLoader = null; - } + performSystemServerDexOpt(systemServerClasspath); // Capturing profiles is only supported for debug or eng builds since selinux normally // prevents it. if (shouldProfileSystemServer() && (Build.IS_USERDEBUG || Build.IS_ENG)) { @@ -546,9 +534,10 @@ public class ZygoteInit { throw new IllegalStateException("Unexpected return from WrapperInit.execApplication"); } else { - createSystemServerClassLoader(); - ClassLoader cl = sCachedSystemServerClassLoader; - if (cl != null) { + ClassLoader cl = null; + if (systemServerClasspath != null) { + cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion); + Thread.currentThread().setContextClassLoader(cl); } @@ -564,24 +553,6 @@ public class ZygoteInit { } /** - * Create the classloader for the system server and store it in - * {@link sCachedSystemServerClassLoader}. This function may be called through JNI in - * system server startup, when the runtime is in a critically low state. Do not do - * extended computation etc here. - */ - private static void createSystemServerClassLoader() { - if (sCachedSystemServerClassLoader != null) { - return; - } - final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH"); - // TODO: Should we run optimization here? - if (systemServerClasspath != null) { - sCachedSystemServerClassLoader = createPathClassLoader(systemServerClasspath, - VMRuntime.SDK_VERSION_CUR_DEVELOPMENT); - } - } - - /** * Note that preparing the profiles for system server does not require special selinux * permissions. From the installer perspective the system server is a regular package which can * capture profile information. @@ -645,16 +616,15 @@ public class ZygoteInit { /** * Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction - * set of the current runtime. If something was compiled, return true. + * set of the current runtime. */ - private static boolean performSystemServerDexOpt(String classPath) { + private static void performSystemServerDexOpt(String classPath) { final String[] classPathElements = classPath.split(":"); final IInstalld installd = IInstalld.Stub .asInterface(ServiceManager.getService("installd")); final String instructionSet = VMRuntime.getRuntime().vmInstructionSet(); String classPathForElement = ""; - boolean compiledSomething = false; for (String classPathElement : classPathElements) { // We default to the verify filter because the compilation will happen on /data and // system server cannot load executable code outside /system. @@ -695,7 +665,6 @@ public class ZygoteInit { uuid, classLoaderContext, seInfo, false /* downgrade */, targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null, "server-dexopt"); - compiledSomething = true; } catch (RemoteException | ServiceSpecificException e) { // Ignore (but log), we need this on the classpath for fallback mode. Log.w(TAG, "Failed compiling classpath element for system server: " @@ -706,8 +675,6 @@ public class ZygoteInit { classPathForElement = encodeSystemServerClassPath( classPathForElement, classPathElement); } - - return compiledSomething; } /** diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java index 7f567b948eb2..dd64c402fdbf 100644 --- a/core/java/com/android/internal/util/Parcelling.java +++ b/core/java/com/android/internal/util/Parcelling.java @@ -221,6 +221,33 @@ public interface Parcelling<T> { } } + class ForInternedStringArraySet implements Parcelling<ArraySet<String>> { + @Override + public void parcel(ArraySet<String> item, Parcel dest, int parcelFlags) { + if (item == null) { + dest.writeInt(-1); + } else { + dest.writeInt(item.size()); + for (String string : item) { + dest.writeString(string); + } + } + } + + @Override + public ArraySet<String> unparcel(Parcel source) { + final int size = source.readInt(); + if (size < 0) { + return null; + } + ArraySet<String> set = new ArraySet<>(); + for (int count = 0; count < size; count++) { + set.add(TextUtils.safeIntern(source.readString())); + } + return set; + } + } + class ForBoolean implements Parcelling<Boolean> { @Override public void parcel(@Nullable Boolean item, Parcel dest, int parcelFlags) { diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 5dd3389b5d4c..47f094f292f2 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -116,7 +116,8 @@ public class BaseIWindow extends IWindow.Stub { } @Override - public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync) { + public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom, + boolean sync) { if (sync) { try { mSession.wallpaperOffsetsComplete(asBinder()); diff --git a/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl index 29bdf5661eaf..feb3f026b806 100644 --- a/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl +++ b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl @@ -24,4 +24,6 @@ import android.view.SurfaceControlViewHost; */ oneway interface IInlineContentCallback { void onContent(in SurfaceControlViewHost.SurfacePackage content); + void onClick(); + void onLongClick(); } diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java index 74ad81566ef4..bd0623e1144e 100644 --- a/core/java/com/android/internal/widget/CachingIconView.java +++ b/core/java/com/android/internal/widget/CachingIconView.java @@ -32,6 +32,7 @@ import android.widget.ImageView; import android.widget.RemoteViews; import java.util.Objects; +import java.util.function.Consumer; /** * An ImageView for displaying an Icon. Avoids reloading the Icon when possible. @@ -44,6 +45,7 @@ public class CachingIconView extends ImageView { private boolean mInternalSetDrawable; private boolean mForceHidden; private int mDesiredVisibility; + private Consumer<Integer> mOnVisibilityChangedListener; @UnsupportedAppUsage public CachingIconView(Context context, @Nullable AttributeSet attrs) { @@ -198,6 +200,13 @@ public class CachingIconView extends ImageView { private void updateVisibility() { int visibility = mDesiredVisibility == VISIBLE && mForceHidden ? INVISIBLE : mDesiredVisibility; + if (mOnVisibilityChangedListener != null) { + mOnVisibilityChangedListener.accept(visibility); + } super.setVisibility(visibility); } + + public void setOnVisibilityChangedListener(Consumer<Integer> listener) { + mOnVisibilityChangedListener = listener; + } } diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java new file mode 100644 index 000000000000..07be113d9a53 --- /dev/null +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -0,0 +1,866 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.annotation.AttrRes; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StyleRes; +import android.app.Notification; +import android.app.Person; +import android.app.RemoteInputHistoryItem; +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.Gravity; +import android.view.RemotableViewMethod; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.RemoteViews; +import android.widget.TextView; + +import com.android.internal.R; +import com.android.internal.graphics.ColorUtils; +import com.android.internal.util.ContrastColorUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.regex.Pattern; + +/** + * A custom-built layout for the Notification.MessagingStyle allows dynamic addition and removal + * messages and adapts the layout accordingly. + */ +@RemoteViews.RemoteView +public class ConversationLayout extends FrameLayout + implements ImageMessageConsumer, IMessagingLayout { + + public static final boolean CONVERSATION_LAYOUT_ENABLED = true; + private static final float COLOR_SHIFT_AMOUNT = 60; + /** + * Pattren for filter some ingonable characters. + * p{Z} for any kind of whitespace or invisible separator. + * p{C} for any kind of punctuation character. + */ + private static final Pattern IGNORABLE_CHAR_PATTERN + = Pattern.compile("[\\p{C}\\p{Z}]"); + private static final Pattern SPECIAL_CHAR_PATTERN + = Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); + private static final Consumer<MessagingMessage> REMOVE_MESSAGE + = MessagingMessage::removeMessage; + public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f); + public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); + public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + public static final OnLayoutChangeListener MESSAGING_PROPERTY_ANIMATOR + = new MessagingPropertyAnimator(); + private List<MessagingMessage> mMessages = new ArrayList<>(); + private List<MessagingMessage> mHistoricMessages = new ArrayList<>(); + private MessagingLinearLayout mMessagingLinearLayout; + private boolean mShowHistoricMessages; + private ArrayList<MessagingGroup> mGroups = new ArrayList<>(); + private TextView mTitleView; + private int mLayoutColor; + private int mSenderTextColor; + private int mMessageTextColor; + private int mAvatarSize; + private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint mTextPaint = new Paint(); + private Icon mAvatarReplacement; + private boolean mIsOneToOne; + private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>(); + private Person mUser; + private CharSequence mNameReplacement; + private boolean mIsCollapsed; + private ImageResolver mImageResolver; + private ImageView mConversationIcon; + private TextView mConversationText; + private View mConversationIconBadge; + private Icon mLargeIcon; + private View mExpandButtonContainer; + private ViewGroup mExpandButtonAndContentContainer; + private NotificationExpandButton mExpandButton; + private int mExpandButtonExpandedTopMargin; + private int mBadgedSideMargins; + private int mIconSizeBadged; + private int mIconSizeCentered; + private CachingIconView mIcon; + private int mExpandedGroupTopMargin; + private int mExpandButtonExpandedSize; + private View mConversationFacePile; + private int mNotificationBackgroundColor; + private CharSequence mFallbackChatName; + private CharSequence mFallbackGroupChatName; + private CharSequence mConversationTitle; + + public ConversationLayout(@NonNull Context context) { + super(context); + } + + public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mMessagingLinearLayout = findViewById(R.id.notification_messaging); + mMessagingLinearLayout.setMessagingLayout(this); + // We still want to clip, but only on the top, since views can temporarily out of bounds + // during transitions. + DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); + int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels); + Rect rect = new Rect(0, 0, size, size); + mMessagingLinearLayout.setClipBounds(rect); + mTitleView = findViewById(R.id.title); + mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size); + mTextPaint.setTextAlign(Paint.Align.CENTER); + mTextPaint.setAntiAlias(true); + mConversationIcon = findViewById(R.id.conversation_icon); + mIcon = findViewById(R.id.icon); + mConversationIconBadge = findViewById(R.id.conversation_icon_badge); + mIcon.setOnVisibilityChangedListener((visibility) -> { + // Always keep the badge visibility in sync with the icon. This is necessary in cases + // Where the icon is being hidden externally like in group children. + mConversationIconBadge.setVisibility(visibility); + }); + mConversationText = findViewById(R.id.conversation_text); + mExpandButtonContainer = findViewById(R.id.expand_button_container); + mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container); + mExpandButton = findViewById(R.id.expand_button); + mExpandButtonExpandedTopMargin = getResources().getDimensionPixelSize( + R.dimen.conversation_expand_button_top_margin_expanded); + mExpandButtonExpandedSize = getResources().getDimensionPixelSize( + R.dimen.conversation_expand_button_expanded_size); + mBadgedSideMargins = getResources().getDimensionPixelSize( + R.dimen.conversation_badge_side_margin); + mIconSizeBadged = getResources().getDimensionPixelSize( + R.dimen.conversation_icon_size_badged); + mIconSizeCentered = getResources().getDimensionPixelSize( + R.dimen.conversation_icon_size_centered); + mExpandedGroupTopMargin = getResources().getDimensionPixelSize( + R.dimen.conversation_icon_margin_top_centered); + mConversationFacePile = findViewById(R.id.conversation_face_pile); + mFallbackChatName = getResources().getString( + R.string.conversation_title_fallback_one_to_one); + mFallbackGroupChatName = getResources().getString( + R.string.conversation_title_fallback_group_chat); + } + + @RemotableViewMethod + public void setAvatarReplacement(Icon icon) { + mAvatarReplacement = icon; + } + + @RemotableViewMethod + public void setNameReplacement(CharSequence nameReplacement) { + mNameReplacement = nameReplacement; + } + + /** + * Set this layout to show the collapsed representation. + * + * @param isCollapsed is it collapsed + */ + @RemotableViewMethod + public void setIsCollapsed(boolean isCollapsed) { + mIsCollapsed = isCollapsed; + mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE); + updateExpandButton(); + } + + @RemotableViewMethod + public void setData(Bundle extras) { + Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); + List<Notification.MessagingStyle.Message> newMessages + = Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages); + Parcelable[] histMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES); + List<Notification.MessagingStyle.Message> newHistoricMessages + = Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages); + + // mUser now set (would be nice to avoid the side effect but WHATEVER) + setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON)); + + + // Append remote input history to newMessages (again, side effect is lame but WHATEVS) + RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[]) + extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + addRemoteInputHistoryToMessages(newMessages, history); + + boolean showSpinner = + extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); + + // bind it, baby + bind(newMessages, newHistoricMessages, showSpinner); + } + + @Override + public void setImageResolver(ImageResolver resolver) { + mImageResolver = resolver; + } + + private void addRemoteInputHistoryToMessages( + List<Notification.MessagingStyle.Message> newMessages, + RemoteInputHistoryItem[] remoteInputHistory) { + if (remoteInputHistory == null || remoteInputHistory.length == 0) { + return; + } + for (int i = remoteInputHistory.length - 1; i >= 0; i--) { + RemoteInputHistoryItem historyMessage = remoteInputHistory[i]; + Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message( + historyMessage.getText(), 0, (Person) null, true /* remoteHistory */); + if (historyMessage.getUri() != null) { + message.setData(historyMessage.getMimeType(), historyMessage.getUri()); + } + newMessages.add(message); + } + } + + private void bind(List<Notification.MessagingStyle.Message> newMessages, + List<Notification.MessagingStyle.Message> newHistoricMessages, + boolean showSpinner) { + // convert MessagingStyle.Message to MessagingMessage, re-using ones from a previous binding + // if they exist + List<MessagingMessage> historicMessages = createMessages(newHistoricMessages, + true /* isHistoric */); + List<MessagingMessage> messages = createMessages(newMessages, false /* isHistoric */); + + // Copy our groups, before they get clobbered + ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups); + + // Add our new MessagingMessages to groups + List<List<MessagingMessage>> groups = new ArrayList<>(); + List<Person> senders = new ArrayList<>(); + + // Lets first find the groups (populate `groups` and `senders`) + findGroups(historicMessages, messages, groups, senders); + + // Let's now create the views and reorder them accordingly + // side-effect: updates mGroups, mAddedGroups + createGroupViews(groups, senders, showSpinner); + + // Let's first check which groups were removed altogether and remove them in one animation + removeGroups(oldGroups); + + // Let's remove the remaining messages + mMessages.forEach(REMOVE_MESSAGE); + mHistoricMessages.forEach(REMOVE_MESSAGE); + + mMessages = messages; + mHistoricMessages = historicMessages; + + updateHistoricMessageVisibility(); + updateTitleAndNamesDisplay(); + + updateConversationLayout(); + + } + + /** + * Update the layout according to the data provided (i.e mIsOneToOne, expanded etc); + */ + private void updateConversationLayout() { + // TODO: resolve this from shortcuts + // Set avatar and name + CharSequence conversationText = mConversationTitle; + // TODO: display the secondary text somewhere + if (mIsOneToOne) { + // Let's resolve the icon / text from the last sender + mConversationIcon.setVisibility(VISIBLE); + mConversationFacePile.setVisibility(GONE); + CharSequence userKey = getKey(mUser); + for (int i = mGroups.size() - 1; i >= 0; i--) { + MessagingGroup messagingGroup = mGroups.get(i); + Person messageSender = messagingGroup.getSender(); + if ((messageSender != null && !TextUtils.equals(userKey, getKey(messageSender))) + || i == 0) { + if (TextUtils.isEmpty(conversationText)) { + // We use the sendername as header text if no conversation title is provided + // (This usually happens for most 1:1 conversations) + conversationText = messagingGroup.getSenderName(); + } + Icon avatarIcon = messagingGroup.getAvatarIcon(); + if (avatarIcon == null) { + avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor); + } + mConversationIcon.setImageIcon(avatarIcon); + break; + } + } + } else { + if (mIsCollapsed) { + if (mLargeIcon != null) { + mConversationIcon.setVisibility(VISIBLE); + mConversationFacePile.setVisibility(GONE); + mConversationIcon.setImageIcon(mLargeIcon); + } else { + mConversationIcon.setVisibility(GONE); + // This will also inflate it! + mConversationFacePile.setVisibility(VISIBLE); + mConversationFacePile = findViewById(R.id.conversation_face_pile); + bindFacePile(); + } + } else { + mConversationFacePile.setVisibility(GONE); + mConversationIcon.setVisibility(GONE); + } + } + if (TextUtils.isEmpty(conversationText)) { + conversationText = mIsOneToOne ? mFallbackChatName : mFallbackGroupChatName; + } + mConversationText.setText(conversationText); + // Update if the groups can hide the sender if they are first (applies to 1:1 conversations) + // This needs to happen after all of the above o update all of the groups + for (int i = mGroups.size() - 1; i >= 0; i--) { + MessagingGroup messagingGroup = mGroups.get(i); + CharSequence messageSender = messagingGroup.getSenderName(); + boolean canHide = mIsOneToOne + && TextUtils.equals(conversationText, messageSender); + messagingGroup.setCanHideSenderIfFirst(canHide); + } + updateIconPositionAndSize(); + } + + private void bindFacePile() { + // Let's bind the face pile + View bottomBackground = mConversationFacePile.findViewById( + R.id.conversation_face_pile_bottom_background); + applyNotificationBackgroundColor(bottomBackground); + ImageView bottomView = mConversationFacePile.findViewById( + R.id.conversation_face_pile_bottom); + ImageView topView = mConversationFacePile.findViewById( + R.id.conversation_face_pile_top); + // Let's find the two last conversations: + Icon secondLastIcon = null; + CharSequence lastKey = null; + Icon lastIcon = null; + CharSequence userKey = getKey(mUser); + for (int i = mGroups.size() - 1; i >= 0; i--) { + MessagingGroup messagingGroup = mGroups.get(i); + Person messageSender = messagingGroup.getSender(); + boolean notUser = messageSender != null + && !TextUtils.equals(userKey, getKey(messageSender)); + boolean notIncluded = messageSender != null + && !TextUtils.equals(lastKey, getKey(messageSender)); + if ((notUser && notIncluded) + || (i == 0 && lastKey == null)) { + if (lastIcon == null) { + lastIcon = messagingGroup.getAvatarIcon(); + lastKey = getKey(messageSender); + } else { + secondLastIcon = messagingGroup.getAvatarIcon(); + break; + } + } + } + if (lastIcon == null) { + lastIcon = createAvatarSymbol(" ", "", mLayoutColor); + } + bottomView.setImageIcon(lastIcon); + if (secondLastIcon == null) { + secondLastIcon = createAvatarSymbol("", "", mLayoutColor); + } + topView.setImageIcon(secondLastIcon); + } + + /** + * update the icon position and sizing + */ + private void updateIconPositionAndSize() { + int gravity; + int marginStart; + int marginTop; + int iconSize; + if (mIsOneToOne || mIsCollapsed) { + // Baded format + gravity = Gravity.LEFT; + marginStart = mBadgedSideMargins; + marginTop = mBadgedSideMargins; + iconSize = mIconSizeBadged; + } else { + gravity = Gravity.CENTER_HORIZONTAL; + marginStart = 0; + marginTop = mExpandedGroupTopMargin; + iconSize = mIconSizeCentered; + } + LayoutParams layoutParams = + (LayoutParams) mConversationIconBadge.getLayoutParams(); + layoutParams.gravity = gravity; + layoutParams.topMargin = marginTop; + layoutParams.setMarginStart(marginStart); + mConversationIconBadge.setLayoutParams(layoutParams); + ViewGroup.LayoutParams iconParams = mIcon.getLayoutParams(); + iconParams.width = iconSize; + iconParams.height = iconSize; + mIcon.setLayoutParams(iconParams); + } + + @RemotableViewMethod + public void setLargeIcon(Icon largeIcon) { + mLargeIcon = largeIcon; + } + + /** + * Sets the conversation title of this conversation. + * + * @param conversationTitle the conversation title + */ + @RemotableViewMethod + public void setConversationTitle(CharSequence conversationTitle) { + mConversationTitle = conversationTitle; + } + + private void removeGroups(ArrayList<MessagingGroup> oldGroups) { + int size = oldGroups.size(); + for (int i = 0; i < size; i++) { + MessagingGroup group = oldGroups.get(i); + if (!mGroups.contains(group)) { + List<MessagingMessage> messages = group.getMessages(); + Runnable endRunnable = () -> { + mMessagingLinearLayout.removeTransientView(group); + group.recycle(); + }; + + boolean wasShown = group.isShown(); + mMessagingLinearLayout.removeView(group); + if (wasShown && !MessagingLinearLayout.isGone(group)) { + mMessagingLinearLayout.addTransientView(group, 0); + group.removeGroupAnimated(endRunnable); + } else { + endRunnable.run(); + } + mMessages.removeAll(messages); + mHistoricMessages.removeAll(messages); + } + } + } + + private void updateTitleAndNamesDisplay() { + ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>(); + ArrayMap<Character, CharSequence> uniqueCharacters = new ArrayMap<>(); + for (int i = 0; i < mGroups.size(); i++) { + MessagingGroup group = mGroups.get(i); + CharSequence senderName = group.getSenderName(); + if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) { + continue; + } + if (!uniqueNames.containsKey(senderName)) { + // Only use visible characters to get uniqueNames + String pureSenderName = IGNORABLE_CHAR_PATTERN + .matcher(senderName).replaceAll("" /* replacement */); + char c = pureSenderName.charAt(0); + if (uniqueCharacters.containsKey(c)) { + // this character was already used, lets make it more unique. We first need to + // resolve the existing character if it exists + CharSequence existingName = uniqueCharacters.get(c); + if (existingName != null) { + uniqueNames.put(existingName, findNameSplit((String) existingName)); + uniqueCharacters.put(c, null); + } + uniqueNames.put(senderName, findNameSplit((String) senderName)); + } else { + uniqueNames.put(senderName, Character.toString(c)); + uniqueCharacters.put(c, pureSenderName); + } + } + } + + // Now that we have the correct symbols, let's look what we have cached + ArrayMap<CharSequence, Icon> cachedAvatars = new ArrayMap<>(); + for (int i = 0; i < mGroups.size(); i++) { + // Let's now set the avatars + MessagingGroup group = mGroups.get(i); + boolean isOwnMessage = group.getSender() == mUser; + CharSequence senderName = group.getSenderName(); + if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName) + || (mIsOneToOne && mAvatarReplacement != null && !isOwnMessage)) { + continue; + } + String symbol = uniqueNames.get(senderName); + Icon cachedIcon = group.getAvatarSymbolIfMatching(senderName, + symbol, mLayoutColor); + if (cachedIcon != null) { + cachedAvatars.put(senderName, cachedIcon); + } + } + + for (int i = 0; i < mGroups.size(); i++) { + // Let's now set the avatars + MessagingGroup group = mGroups.get(i); + CharSequence senderName = group.getSenderName(); + if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) { + continue; + } + if (mIsOneToOne && mAvatarReplacement != null && group.getSender() != mUser) { + group.setAvatar(mAvatarReplacement); + } else { + Icon cachedIcon = cachedAvatars.get(senderName); + if (cachedIcon == null) { + cachedIcon = createAvatarSymbol(senderName, uniqueNames.get(senderName), + mLayoutColor); + cachedAvatars.put(senderName, cachedIcon); + } + group.setCreatedAvatar(cachedIcon, senderName, uniqueNames.get(senderName), + mLayoutColor); + } + } + } + + private Icon createAvatarSymbol(CharSequence senderName, String symbol, int layoutColor) { + if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) || + SPECIAL_CHAR_PATTERN.matcher(symbol).find()) { + Icon avatarIcon = Icon.createWithResource(getContext(), + R.drawable.messaging_user); + avatarIcon.setTint(findColor(senderName, layoutColor)); + return avatarIcon; + } else { + Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + float radius = mAvatarSize / 2.0f; + int color = findColor(senderName, layoutColor); + mPaint.setColor(color); + canvas.drawCircle(radius, radius, radius, mPaint); + boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f; + mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE); + mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f); + int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2)); + canvas.drawText(symbol, radius, yPos, mTextPaint); + return Icon.createWithBitmap(bitmap); + } + } + + private int findColor(CharSequence senderName, int layoutColor) { + double luminance = ContrastColorUtil.calculateLuminance(layoutColor); + float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f; + + // we need to offset the range if the luminance is too close to the borders + shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0); + shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0); + return ContrastColorUtil.getShiftedColor(layoutColor, + (int) (shift * COLOR_SHIFT_AMOUNT)); + } + + private String findNameSplit(String existingName) { + String[] split = existingName.split(" "); + if (split.length > 1) { + return Character.toString(split[0].charAt(0)) + + Character.toString(split[1].charAt(0)); + } + return existingName.substring(0, 1); + } + + @RemotableViewMethod + public void setLayoutColor(int color) { + mLayoutColor = color; + } + + @RemotableViewMethod + public void setIsOneToOne(boolean oneToOne) { + mIsOneToOne = oneToOne; + } + + @RemotableViewMethod + public void setSenderTextColor(int color) { + mSenderTextColor = color; + } + + /** + * @param color the color of the notification background + */ + @RemotableViewMethod + public void setNotificationBackgroundColor(int color) { + mNotificationBackgroundColor = color; + applyNotificationBackgroundColor(mConversationIconBadge); + } + + private void applyNotificationBackgroundColor(View view) { + view.setBackgroundTintList(ColorStateList.valueOf(mNotificationBackgroundColor)); + } + + @RemotableViewMethod + public void setMessageTextColor(int color) { + mMessageTextColor = color; + } + + private void setUser(Person user) { + mUser = user; + if (mUser.getIcon() == null) { + Icon userIcon = Icon.createWithResource(getContext(), + R.drawable.messaging_user); + userIcon.setTint(mLayoutColor); + mUser = mUser.toBuilder().setIcon(userIcon).build(); + } + } + + private void createGroupViews(List<List<MessagingMessage>> groups, + List<Person> senders, boolean showSpinner) { + mGroups.clear(); + for (int groupIndex = 0; groupIndex < groups.size(); groupIndex++) { + List<MessagingMessage> group = groups.get(groupIndex); + MessagingGroup newGroup = null; + // we'll just take the first group that exists or create one there is none + for (int messageIndex = group.size() - 1; messageIndex >= 0; messageIndex--) { + MessagingMessage message = group.get(messageIndex); + newGroup = message.getGroup(); + if (newGroup != null) { + break; + } + } + // Create a new group, adding it to the linear layout as well + if (newGroup == null) { + newGroup = MessagingGroup.createGroup(mMessagingLinearLayout); + mAddedGroups.add(newGroup); + } + newGroup.setDisplayImagesAtEnd(mIsCollapsed); + newGroup.setLayoutColor(mLayoutColor); + newGroup.setTextColors(mSenderTextColor, mMessageTextColor); + Person sender = senders.get(groupIndex); + CharSequence nameOverride = null; + if (sender != mUser && mNameReplacement != null) { + nameOverride = mNameReplacement; + } + newGroup.setShowingAvatar(!mIsOneToOne && !mIsCollapsed); + newGroup.setSingleLine(mIsCollapsed); + newGroup.setSender(sender, nameOverride); + newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner); + mGroups.add(newGroup); + + // Reposition to the correct place (if we're re-using a group) + if (mMessagingLinearLayout.indexOfChild(newGroup) != groupIndex) { + mMessagingLinearLayout.removeView(newGroup); + mMessagingLinearLayout.addView(newGroup, groupIndex); + } + newGroup.setMessages(group); + } + } + + private void findGroups(List<MessagingMessage> historicMessages, + List<MessagingMessage> messages, List<List<MessagingMessage>> groups, + List<Person> senders) { + CharSequence currentSenderKey = null; + List<MessagingMessage> currentGroup = null; + int histSize = historicMessages.size(); + for (int i = 0; i < histSize + messages.size(); i++) { + MessagingMessage message; + if (i < histSize) { + message = historicMessages.get(i); + } else { + message = messages.get(i - histSize); + } + boolean isNewGroup = currentGroup == null; + Person sender = message.getMessage().getSenderPerson(); + CharSequence key = getKey(sender); + isNewGroup |= !TextUtils.equals(key, currentSenderKey); + if (isNewGroup) { + currentGroup = new ArrayList<>(); + groups.add(currentGroup); + if (sender == null) { + sender = mUser; + } + senders.add(sender); + currentSenderKey = key; + } + currentGroup.add(message); + } + } + + private CharSequence getKey(Person person) { + return person == null ? null : person.getKey() == null ? person.getName() : person.getKey(); + } + + /** + * Creates new messages, reusing existing ones if they are available. + * + * @param newMessages the messages to parse. + */ + private List<MessagingMessage> createMessages( + List<Notification.MessagingStyle.Message> newMessages, boolean historic) { + List<MessagingMessage> result = new ArrayList<>(); + for (int i = 0; i < newMessages.size(); i++) { + Notification.MessagingStyle.Message m = newMessages.get(i); + MessagingMessage message = findAndRemoveMatchingMessage(m); + if (message == null) { + message = MessagingMessage.createMessage(this, m, mImageResolver); + } + message.setIsHistoric(historic); + result.add(message); + } + return result; + } + + private MessagingMessage findAndRemoveMatchingMessage(Notification.MessagingStyle.Message m) { + for (int i = 0; i < mMessages.size(); i++) { + MessagingMessage existing = mMessages.get(i); + if (existing.sameAs(m)) { + mMessages.remove(i); + return existing; + } + } + for (int i = 0; i < mHistoricMessages.size(); i++) { + MessagingMessage existing = mHistoricMessages.get(i); + if (existing.sameAs(m)) { + mHistoricMessages.remove(i); + return existing; + } + } + return null; + } + + public void showHistoricMessages(boolean show) { + mShowHistoricMessages = show; + updateHistoricMessageVisibility(); + } + + private void updateHistoricMessageVisibility() { + int numHistoric = mHistoricMessages.size(); + for (int i = 0; i < numHistoric; i++) { + MessagingMessage existing = mHistoricMessages.get(i); + existing.setVisibility(mShowHistoricMessages ? VISIBLE : GONE); + } + int numGroups = mGroups.size(); + for (int i = 0; i < numGroups; i++) { + MessagingGroup group = mGroups.get(i); + int visibleChildren = 0; + List<MessagingMessage> messages = group.getMessages(); + int numGroupMessages = messages.size(); + for (int j = 0; j < numGroupMessages; j++) { + MessagingMessage message = messages.get(j); + if (message.getVisibility() != GONE) { + visibleChildren++; + } + } + if (visibleChildren > 0 && group.getVisibility() == GONE) { + group.setVisibility(VISIBLE); + } else if (visibleChildren == 0 && group.getVisibility() != GONE) { + group.setVisibility(GONE); + } + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (!mAddedGroups.isEmpty()) { + getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + for (MessagingGroup group : mAddedGroups) { + if (!group.isShown()) { + continue; + } + MessagingPropertyAnimator.fadeIn(group.getAvatar()); + MessagingPropertyAnimator.fadeIn(group.getSenderView()); + MessagingPropertyAnimator.startLocalTranslationFrom(group, + group.getHeight(), LINEAR_OUT_SLOW_IN); + } + mAddedGroups.clear(); + getViewTreeObserver().removeOnPreDrawListener(this); + return true; + } + }); + } + } + + public MessagingLinearLayout getMessagingLinearLayout() { + return mMessagingLinearLayout; + } + + public ArrayList<MessagingGroup> getMessagingGroups() { + return mGroups; + } + + private void updateExpandButton() { + int drawableId; + int contentDescriptionId; + int gravity; + int topMargin = 0; + ViewGroup newContainer; + int newContainerHeight; + if (mIsCollapsed) { + drawableId = R.drawable.ic_expand_notification; + contentDescriptionId = R.string.expand_button_content_description_collapsed; + gravity = Gravity.CENTER; + newContainer = mExpandButtonAndContentContainer; + newContainerHeight = LayoutParams.MATCH_PARENT; + } else { + drawableId = R.drawable.ic_collapse_notification; + contentDescriptionId = R.string.expand_button_content_description_expanded; + gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; + topMargin = mExpandButtonExpandedTopMargin; + newContainer = this; + newContainerHeight = mExpandButtonExpandedSize; + } + mExpandButton.setImageDrawable(getContext().getDrawable(drawableId)); + mExpandButton.setColorFilter(mExpandButton.getOriginalNotificationColor()); + + // We need to make sure that the expand button is in the linearlayout pushing over the + // content when collapsed, but allows the content to flow under it when expanded. + if (newContainer != mExpandButtonContainer.getParent()) { + ((ViewGroup) mExpandButtonContainer.getParent()).removeView(mExpandButtonContainer); + newContainer.addView(mExpandButtonContainer); + MarginLayoutParams layoutParams = + (MarginLayoutParams) mExpandButtonContainer.getLayoutParams(); + layoutParams.height = newContainerHeight; + mExpandButtonContainer.setLayoutParams(layoutParams); + } + + // update if the expand button is centered + FrameLayout.LayoutParams layoutParams = (LayoutParams) mExpandButton.getLayoutParams(); + layoutParams.gravity = gravity; + layoutParams.topMargin = topMargin; + mExpandButton.setLayoutParams(layoutParams); + + mExpandButtonContainer.setContentDescription(mContext.getText(contentDescriptionId)); + + } + + public void updateExpandability(boolean expandable, @Nullable OnClickListener onClickListener) { + if (expandable) { + mExpandButtonContainer.setVisibility(VISIBLE); + mExpandButtonContainer.setOnClickListener(onClickListener); + } else { + // TODO: handle content paddings to end of layout + mExpandButtonContainer.setVisibility(GONE); + } + } +} diff --git a/core/java/com/android/internal/widget/IMessagingLayout.java b/core/java/com/android/internal/widget/IMessagingLayout.java new file mode 100644 index 000000000000..149d05641a0b --- /dev/null +++ b/core/java/com/android/internal/widget/IMessagingLayout.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.internal.widget; + +import android.content.Context; + +import java.util.ArrayList; + +/** + * An interface for a MessagingLayout + */ +public interface IMessagingLayout { + + /** + * @return the layout containing the messages + */ + MessagingLinearLayout getMessagingLinearLayout(); + + /** + * @return the context of this view + */ + Context getContext(); + + /** + * @return the list of messaging groups + */ + ArrayList<MessagingGroup> getMessagingGroups(); +} diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java index c9a916187d33..99779032eebc 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -55,8 +55,9 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou private static Pools.SimplePool<MessagingGroup> sInstancePool = new Pools.SynchronizedPool<>(10); private MessagingLinearLayout mMessageContainer; - private ImageFloatingTextView mSenderName; + ImageFloatingTextView mSenderView; private ImageView mAvatarView; + private View mAvatarContainer; private String mAvatarSymbol = ""; private int mLayoutColor; private CharSequence mAvatarName = ""; @@ -72,10 +73,18 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou private boolean mImagesAtEnd; private ViewGroup mImageContainer; private MessagingImageMessage mIsolatedMessage; - private boolean mTransformingImages; + private boolean mClippingDisabled; private Point mDisplaySize = new Point(); private ProgressBar mSendingSpinner; private View mSendingSpinnerContainer; + private boolean mShowingAvatar = true; + private CharSequence mSenderName; + private boolean mSingleLine = false; + private LinearLayout mContentContainer; + private int mRequestedMaxDisplayedLines = Integer.MAX_VALUE; + private int mSenderTextPaddingSingleLine; + private boolean mIsFirstGroupInLayout = true; + private boolean mCanHideSenderIfFirst; public MessagingGroup(@NonNull Context context) { super(context); @@ -99,26 +108,34 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou protected void onFinishInflate() { super.onFinishInflate(); mMessageContainer = findViewById(R.id.group_message_container); - mSenderName = findViewById(R.id.message_name); + mSenderView = findViewById(R.id.message_name); mAvatarView = findViewById(R.id.message_icon); mImageContainer = findViewById(R.id.messaging_group_icon_container); mSendingSpinner = findViewById(R.id.messaging_group_sending_progress); + mContentContainer = findViewById(R.id.messaging_group_content_container); mSendingSpinnerContainer = findViewById(R.id.messaging_group_sending_progress_container); DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); mDisplaySize.x = displayMetrics.widthPixels; mDisplaySize.y = displayMetrics.heightPixels; + mSenderTextPaddingSingleLine = getResources().getDimensionPixelSize( + R.dimen.messaging_group_singleline_sender_padding_end); } public void updateClipRect() { // We want to clip to the senderName if it's available, otherwise our images will come // from a weird position Rect clipRect; - if (mSenderName.getVisibility() != View.GONE && !mTransformingImages) { - ViewGroup parent = (ViewGroup) mSenderName.getParent(); - int top = getDistanceFromParent(mSenderName, parent) - getDistanceFromParent( - mMessageContainer, parent) + mSenderName.getHeight(); + if (mSenderView.getVisibility() != View.GONE && !mClippingDisabled) { + int top; + if (mSingleLine) { + top = 0; + } else { + top = getDistanceFromParent(mSenderView, mContentContainer) + - getDistanceFromParent(mMessageContainer, mContentContainer) + + mSenderView.getHeight(); + } int size = Math.max(mDisplaySize.x, mDisplaySize.y); - clipRect = new Rect(0, top, size, size); + clipRect = new Rect(-size, top, size, size); } else { clipRect = null; } @@ -140,17 +157,31 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou if (nameOverride == null) { nameOverride = sender.getName(); } - mSenderName.setText(nameOverride); + mSenderName = nameOverride; + if (mSingleLine && !TextUtils.isEmpty(nameOverride)) { + nameOverride = mContext.getResources().getString( + R.string.conversation_single_line_name_display, nameOverride); + } + mSenderView.setText(nameOverride); mNeedsGeneratedAvatar = sender.getIcon() == null; if (!mNeedsGeneratedAvatar) { setAvatar(sender.getIcon()); } - mAvatarView.setVisibility(VISIBLE); - mSenderName.setVisibility(TextUtils.isEmpty(nameOverride) ? GONE : VISIBLE); + updateSenderVisibility(); + } + + /** + * Should the avatar be shown for this view. + * + * @param showingAvatar should it be shown + */ + public void setShowingAvatar(boolean showingAvatar) { + mAvatarView.setVisibility(showingAvatar ? VISIBLE : GONE); + mShowingAvatar = showingAvatar; } public void setSending(boolean sending) { - int visibility = sending ? View.VISIBLE : View.GONE; + int visibility = sending ? VISIBLE : GONE; if (mSendingSpinnerContainer.getVisibility() != visibility) { mSendingSpinnerContainer.setVisibility(visibility); updateMessageColor(); @@ -171,7 +202,9 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou public void setAvatar(Icon icon) { mAvatarIcon = icon; - mAvatarView.setImageIcon(icon); + if (mShowingAvatar || icon == null) { + mAvatarView.setImageIcon(icon); + } mAvatarSymbol = ""; mAvatarName = ""; } @@ -220,13 +253,20 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou setAvatar(null); mAvatarView.setAlpha(1.0f); mAvatarView.setTranslationY(0.0f); - mSenderName.setAlpha(1.0f); - mSenderName.setTranslationY(0.0f); + mSenderView.setAlpha(1.0f); + mSenderView.setTranslationY(0.0f); setAlpha(1.0f); mIsolatedMessage = null; mMessages = null; + mSenderName = null; mAddedMessages.clear(); mFirstLayout = true; + setCanHideSenderIfFirst(false); + setIsFirstInLayout(true); + + setMaxDisplayedLines(Integer.MAX_VALUE); + setSingleLine(false); + setShowingAvatar(true); MessagingPropertyAnimator.recycle(this); sInstancePool.release(MessagingGroup.this); } @@ -252,7 +292,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } public CharSequence getSenderName() { - return mSenderName.getText(); + return mSenderName; } public static void dropCache() { @@ -310,7 +350,12 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou @Override public void setMaxDisplayedLines(int lines) { - mMessageContainer.setMaxDisplayedLines(lines); + mRequestedMaxDisplayedLines = lines; + updateMaxDisplayedLines(); + } + + private void updateMaxDisplayedLines() { + mMessageContainer.setMaxDisplayedLines(mSingleLine ? 1 : mRequestedMaxDisplayedLines); } @Override @@ -324,6 +369,35 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou return mIsHidingAnimated; } + @Override + public void setIsFirstInLayout(boolean first) { + if (first != mIsFirstGroupInLayout) { + mIsFirstGroupInLayout = first; + updateSenderVisibility(); + } + } + + /** + * @param canHide true if the sender can be hidden if it is first + */ + public void setCanHideSenderIfFirst(boolean canHide) { + if (mCanHideSenderIfFirst != canHide) { + mCanHideSenderIfFirst = canHide; + updateSenderVisibility(); + } + } + + private void updateSenderVisibility() { + boolean hidden = (mIsFirstGroupInLayout || mSingleLine) && mCanHideSenderIfFirst + || TextUtils.isEmpty(mSenderName); + mSenderView.setVisibility(hidden ? GONE : VISIBLE); + } + + @Override + public boolean hasDifferentHeightWhenFirst() { + return mCanHideSenderIfFirst && !mSingleLine && !TextUtils.isEmpty(mSenderName); + } + private void setIsHidingAnimated(boolean isHiding) { ViewParent parent = getParent(); mIsHidingAnimated = isHiding; @@ -362,7 +436,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mTextColor = messageTextColor; mSendingTextColor = calculateSendingTextColor(); updateMessageColor(); - mSenderName.setTextColor(senderTextColor); + mSenderView.setTextColor(senderTextColor); } public void setLayoutColor(int layoutColor) { @@ -506,13 +580,17 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } public View getSenderView() { - return mSenderName; + return mSenderView; } public View getAvatar() { return mAvatarView; } + public Icon getAvatarIcon() { + return mAvatarIcon; + } + public MessagingLinearLayout getMessageContainer() { return mMessageContainer; } @@ -529,8 +607,8 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou return mSender; } - public void setTransformingImages(boolean transformingImages) { - mTransformingImages = transformingImages; + public void setClippingDisabled(boolean disabled) { + mClippingDisabled = disabled; } public void setDisplayImagesAtEnd(boolean atEnd) { @@ -543,4 +621,27 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou public List<MessagingMessage> getMessages() { return mMessages; } + + /** + * Set this layout to be single line and therefore displaying both the sender and the text on + * the same line. + * + * @param singleLine should be layout be single line + */ + public void setSingleLine(boolean singleLine) { + if (singleLine != mSingleLine) { + mSingleLine = singleLine; + mContentContainer.setOrientation( + singleLine ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL); + MarginLayoutParams layoutParams = (MarginLayoutParams) mSenderView.getLayoutParams(); + layoutParams.setMarginEnd(singleLine ? mSenderTextPaddingSingleLine : 0); + updateMaxDisplayedLines(); + updateClipRect(); + updateSenderVisibility(); + } + } + + public boolean isSingleLine() { + return mSingleLine; + } } diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java index 64650a7ebc2f..c243f3b583e5 100644 --- a/core/java/com/android/internal/widget/MessagingImageMessage.java +++ b/core/java/com/android/internal/widget/MessagingImageMessage.java @@ -120,7 +120,7 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage return true; } - static MessagingMessage createMessage(MessagingLayout layout, + static MessagingMessage createMessage(IMessagingLayout layout, Notification.MessagingStyle.Message m, ImageResolver resolver) { MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout(); MessagingImageMessage createdMessage = sInstancePool.acquire(); diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index f6089589a994..503f3f161c96 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -58,7 +58,8 @@ import java.util.regex.Pattern; * messages and adapts the layout accordingly. */ @RemoteViews.RemoteView -public class MessagingLayout extends FrameLayout implements ImageMessageConsumer { +public class MessagingLayout extends FrameLayout + implements ImageMessageConsumer, IMessagingLayout { private static final float COLOR_SHIFT_AMOUNT = 60; /** @@ -143,9 +144,29 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer mNameReplacement = nameReplacement; } + /** + * Set this layout to show the collapsed representation. + * + * @param isCollapsed is it collapsed + */ + @RemotableViewMethod + public void setIsCollapsed(boolean isCollapsed) { + mDisplayImagesAtEnd = isCollapsed; + } + + @RemotableViewMethod + public void setLargeIcon(Icon largeIcon) { + // Unused + } + + /** + * Sets the conversation title of this conversation. + * + * @param conversationTitle the conversation title + */ @RemotableViewMethod - public void setDisplayImagesAtEnd(boolean atEnd) { - mDisplayImagesAtEnd = atEnd; + public void setConversationTitle(CharSequence conversationTitle) { + // Unused } @RemotableViewMethod @@ -371,6 +392,15 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer mSenderTextColor = color; } + + /** + * @param color the color of the notification background + */ + @RemotableViewMethod + public void setNotificationBackgroundColor(int color) { + // Nothing to do with this + } + @RemotableViewMethod public void setMessageTextColor(int color) { mMessageTextColor = color; diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java index 0c8613b460f6..ac04862d9a7d 100644 --- a/core/java/com/android/internal/widget/MessagingLinearLayout.java +++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java @@ -43,7 +43,7 @@ public class MessagingLinearLayout extends ViewGroup { private int mMaxDisplayedLines = Integer.MAX_VALUE; - private MessagingLayout mMessagingLayout; + private IMessagingLayout mMessagingLayout; public MessagingLinearLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -84,6 +84,11 @@ public class MessagingLinearLayout extends ViewGroup { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); lp.hide = true; + if (child instanceof MessagingChild) { + MessagingChild messagingChild = (MessagingChild) child; + // Whenever we encounter the message first, it's always first in the layout + messagingChild.setIsFirstInLayout(true); + } } totalHeight = mPaddingTop + mPaddingBottom; @@ -91,6 +96,11 @@ public class MessagingLinearLayout extends ViewGroup { int linesRemaining = mMaxDisplayedLines; // Starting from the bottom: we measure every view as if it were the only one. If it still // fits, we take it, otherwise we stop there. + MessagingChild previousChild = null; + View previousView = null; + int previousChildHeight = 0; + int previousTotalHeight = 0; + int previousLinesConsumed = 0; for (int i = count - 1; i >= 0 && totalHeight < targetHeight; i--) { if (getChildAt(i).getVisibility() == GONE) { continue; @@ -99,7 +109,16 @@ public class MessagingLinearLayout extends ViewGroup { LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); MessagingChild messagingChild = null; int spacing = mSpacing; + int previousChildIncrease = 0; if (child instanceof MessagingChild) { + // We need to remeasure the previous child again if it's not the first anymore + if (previousChild != null && previousChild.hasDifferentHeightWhenFirst()) { + previousChild.setIsFirstInLayout(false); + measureChildWithMargins(previousView, widthMeasureSpec, 0, heightMeasureSpec, + previousTotalHeight - previousChildHeight); + previousChildIncrease = previousView.getMeasuredHeight() - previousChildHeight; + linesRemaining -= previousChild.getConsumedLines() - previousLinesConsumed; + } messagingChild = (MessagingChild) child; messagingChild.setMaxDisplayedLines(linesRemaining); spacing += messagingChild.getExtraSpacing(); @@ -110,18 +129,26 @@ public class MessagingLinearLayout extends ViewGroup { final int childHeight = child.getMeasuredHeight(); int newHeight = Math.max(totalHeight, totalHeight + childHeight + lp.topMargin + - lp.bottomMargin + spacing); + lp.bottomMargin + spacing + previousChildIncrease); int measureType = MessagingChild.MEASURED_NORMAL; if (messagingChild != null) { measureType = messagingChild.getMeasuredType(); - linesRemaining -= messagingChild.getConsumedLines(); } // We never measure the first item as too small, we want to at least show something. boolean isTooSmall = measureType == MessagingChild.MEASURED_TOO_SMALL && !first; boolean isShortened = measureType == MessagingChild.MEASURED_SHORTENED || measureType == MessagingChild.MEASURED_TOO_SMALL && first; - if (newHeight <= targetHeight && !isTooSmall) { + boolean showView = newHeight <= targetHeight && !isTooSmall; + if (showView) { + if (messagingChild != null) { + previousLinesConsumed = messagingChild.getConsumedLines(); + linesRemaining -= previousLinesConsumed; + previousChild = messagingChild; + previousView = child; + previousChildHeight = childHeight; + previousTotalHeight = totalHeight; + } totalHeight = newHeight; measuredWidth = Math.max(measuredWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin @@ -131,6 +158,16 @@ public class MessagingLinearLayout extends ViewGroup { break; } } else { + // We now became too short, let's make sure to reset any previous views to be first + // and remeasure it. + if (previousChild != null && previousChild.hasDifferentHeightWhenFirst()) { + previousChild.setIsFirstInLayout(true); + // We need to remeasure the previous child again since it became first + measureChildWithMargins(previousView, widthMeasureSpec, 0, heightMeasureSpec, + previousTotalHeight - previousChildHeight); + // The totalHeight is already correct here since we only set it during the + // first pass + } break; } first = false; @@ -255,11 +292,11 @@ public class MessagingLinearLayout extends ViewGroup { mMaxDisplayedLines = numberLines; } - public void setMessagingLayout(MessagingLayout layout) { + public void setMessagingLayout(IMessagingLayout layout) { mMessagingLayout = layout; } - public MessagingLayout getMessagingLayout() { + public IMessagingLayout getMessagingLayout() { return mMessagingLayout; } @@ -273,6 +310,20 @@ public class MessagingLinearLayout extends ViewGroup { void setMaxDisplayedLines(int lines); void hideAnimated(); boolean isHidingAnimated(); + + /** + * Set that this view is first in layout. Relevant and only set if + * {@link #hasDifferentHeightWhenFirst()}. + * @param first is this first? + */ + default void setIsFirstInLayout(boolean first) {} + + /** + * @return if this layout has different height it is first in the layout + */ + default boolean hasDifferentHeightWhenFirst() { + return false; + } default int getExtraSpacing() { return 0; } diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java index c32d3705bba7..8c8437951402 100644 --- a/core/java/com/android/internal/widget/MessagingMessage.java +++ b/core/java/com/android/internal/widget/MessagingMessage.java @@ -32,7 +32,7 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { **/ String IMAGE_MIME_TYPE_PREFIX = "image/"; - static MessagingMessage createMessage(MessagingLayout layout, + static MessagingMessage createMessage(IMessagingLayout layout, Notification.MessagingStyle.Message m, ImageResolver resolver) { if (hasImage(m) && !ActivityManager.isLowRamDeviceStatic()) { return MessagingImageMessage.createMessage(layout, m, resolver); diff --git a/core/java/com/android/internal/widget/MessagingTextMessage.java b/core/java/com/android/internal/widget/MessagingTextMessage.java index 4081a866f993..d778c5967046 100644 --- a/core/java/com/android/internal/widget/MessagingTextMessage.java +++ b/core/java/com/android/internal/widget/MessagingTextMessage.java @@ -26,14 +26,10 @@ import android.text.Layout; import android.util.AttributeSet; import android.util.Pools; import android.view.LayoutInflater; -import android.view.ViewGroup; -import android.view.ViewParent; import android.widget.RemoteViews; import com.android.internal.R; -import java.util.Objects; - /** * A message of a {@link MessagingLayout}. */ @@ -74,7 +70,7 @@ public class MessagingTextMessage extends ImageFloatingTextView implements Messa return true; } - static MessagingMessage createMessage(MessagingLayout layout, + static MessagingMessage createMessage(IMessagingLayout layout, Notification.MessagingStyle.Message m) { MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout(); MessagingTextMessage createdMessage = sInstancePool.acquire(); diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java index 39f82a5fb349..a49980696e6b 100644 --- a/core/java/com/android/internal/widget/NotificationExpandButton.java +++ b/core/java/com/android/internal/widget/NotificationExpandButton.java @@ -20,7 +20,7 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; -import android.view.View; +import android.view.RemotableViewMethod; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Button; import android.widget.ImageView; @@ -32,6 +32,8 @@ import android.widget.RemoteViews; @RemoteViews.RemoteView public class NotificationExpandButton extends ImageView { + private int mOriginalNotificationColor; + public NotificationExpandButton(Context context) { super(context); } @@ -56,6 +58,15 @@ public class NotificationExpandButton extends ImageView { extendRectToMinTouchSize(outRect); } + @RemotableViewMethod + public void setOriginalNotificationColor(int color) { + mOriginalNotificationColor = color; + } + + public int getOriginalNotificationColor() { + return mOriginalNotificationColor; + } + private void extendRectToMinTouchSize(Rect rect) { int touchTargetSize = (int) (getResources().getDisplayMetrics().density * 48); rect.left = rect.centerX() - touchTargetSize / 2; diff --git a/core/java/com/android/internal/widget/RemeasuringLinearLayout.java b/core/java/com/android/internal/widget/RemeasuringLinearLayout.java index e352b45ef413..7b154a54fc85 100644 --- a/core/java/com/android/internal/widget/RemeasuringLinearLayout.java +++ b/core/java/com/android/internal/widget/RemeasuringLinearLayout.java @@ -23,6 +23,8 @@ import android.view.View; import android.widget.LinearLayout; import android.widget.RemoteViews; +import java.util.ArrayList; + /** * A LinearLayout that sets it's height again after the last measure pass. This is needed for * MessagingLayouts where groups need to be able to snap it's height to. @@ -30,6 +32,8 @@ import android.widget.RemoteViews; @RemoteViews.RemoteView public class RemeasuringLinearLayout extends LinearLayout { + private ArrayList<View> mMatchParentViews = new ArrayList<>(); + public RemeasuringLinearLayout(Context context) { super(context); } @@ -53,6 +57,8 @@ public class RemeasuringLinearLayout extends LinearLayout { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int count = getChildCount(); int height = 0; + boolean isVertical = getOrientation() == LinearLayout.VERTICAL; + boolean isWrapContent = getLayoutParams().height == LayoutParams.WRAP_CONTENT; for (int i = 0; i < count; ++i) { final View child = getChildAt(i); if (child == null || child.getVisibility() == View.GONE) { @@ -60,9 +66,25 @@ public class RemeasuringLinearLayout extends LinearLayout { } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - height = Math.max(height, height + child.getMeasuredHeight() + lp.topMargin + - lp.bottomMargin); + if (!isWrapContent || lp.height != LayoutParams.MATCH_PARENT || isVertical) { + int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; + height = Math.max(height, isVertical ? height + childHeight : childHeight); + } else { + // We have match parent children in a wrap content view, let's measure the + // view properly + mMatchParentViews.add(child); + } + } + if (mMatchParentViews.size() > 0) { + int exactHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); + for (View child : mMatchParentViews) { + child.measure(getChildMeasureSpec( + widthMeasureSpec, getPaddingStart() + getPaddingEnd(), + child.getLayoutParams().width), + exactHeightSpec); + } } + mMatchParentViews.clear(); setMeasuredDimension(getMeasuredWidth(), height); } } diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 0d0dc3ede754..f7c6dbd1a65d 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -853,8 +853,11 @@ static jboolean android_os_Debug_isVmapStack(JNIEnv *env, jobject clazz) } cfg_state = CONFIG_UNKNOWN; if (cfg_state == CONFIG_UNKNOWN) { - const std::map<std::string, std::string> configs = - vintf::VintfObject::GetInstance()->getRuntimeInfo()->kernelConfigs(); + auto runtime_info = vintf::VintfObject::GetInstance() + ->getRuntimeInfo(false /* skip cache */, + vintf::RuntimeInfo::FetchFlag::CONFIG_GZ); + CHECK(runtime_info != nullptr) << "Kernel configs cannot be fetched. b/151092221"; + const std::map<std::string, std::string>& configs = runtime_info->kernelConfigs(); std::map<std::string, std::string>::const_iterator it = configs.find("CONFIG_VMAP_STACK"); cfg_state = (it != configs.end() && it->second == "y") ? CONFIG_SET : CONFIG_UNSET; } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 2128f99ff609..dafb37761d0e 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -121,16 +121,11 @@ typedef const std::function<void(std::string)>& fail_fn_t; static pid_t gSystemServerPid = 0; static constexpr const char* kPropFuse = "persist.sys.fuse"; -static constexpr const char* kZygoteClassName = "com/android/internal/os/Zygote"; - +static const char kZygoteClassName[] = "com/android/internal/os/Zygote"; static jclass gZygoteClass; static jmethodID gCallPostForkSystemServerHooks; static jmethodID gCallPostForkChildHooks; -static constexpr const char* kZygoteInitClassName = "com/android/internal/os/ZygoteInit"; -static jclass gZygoteInitClass; -static jmethodID gCreateSystemServerClassLoader; - static bool gIsSecurityEnforced = true; /** @@ -356,10 +351,14 @@ static const std::array<const std::string, MOUNT_EXTERNAL_COUNT> ExternalStorage // Must match values in com.android.internal.os.Zygote. enum RuntimeFlags : uint32_t { - DEBUG_ENABLE_JDWP = 1, - PROFILE_FROM_SHELL = 1 << 15, - MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20), - MEMORY_TAG_LEVEL_TBI = 1 << 19, + DEBUG_ENABLE_JDWP = 1, + PROFILE_FROM_SHELL = 1 << 15, + MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20), + MEMORY_TAG_LEVEL_TBI = 1 << 19, + GWP_ASAN_LEVEL_MASK = (1 << 21) | (1 << 22), + GWP_ASAN_LEVEL_NEVER = 0 << 21, + GWP_ASAN_LEVEL_LOTTERY = 1 << 21, + GWP_ASAN_LEVEL_ALWAYS = 2 << 21, }; enum UnsolicitedZygoteMessageTypes : uint32_t { @@ -1741,6 +1740,18 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, } android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level)); + bool forceEnableGwpAsan = false; + switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) { + default: + case RuntimeFlags::GWP_ASAN_LEVEL_NEVER: + break; + case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS: + forceEnableGwpAsan = true; + [[fallthrough]]; + case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY: + android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan)); + } + if (NeedsNoRandomizeWorkaround()) { // Work around ARM kernel ASLR lossage (http://b/5817320). int old_personality = personality(0xffffffff); @@ -1780,15 +1791,6 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, fail_fn("Error calling post fork system server hooks."); } - // Prefetch the classloader for the system server. This is done early to - // allow a tie-down of the proper system server selinux domain. - env->CallStaticVoidMethod(gZygoteInitClass, gCreateSystemServerClassLoader); - if (env->ExceptionCheck()) { - // Be robust here. The Java code will attempt to create the classloader - // at a later point (but may not have rights to use AoT artifacts). - env->ExceptionClear(); - } - // TODO(oth): Remove hardcoded label here (b/117874058). static const char* kSystemServerLabel = "u:r:system_server:s0"; if (selinux_android_setcon(kSystemServerLabel) != 0) { @@ -2466,13 +2468,6 @@ int register_com_android_internal_os_Zygote(JNIEnv* env) { gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks", "(IZZLjava/lang/String;)V"); - gZygoteInitClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteInitClassName)); - gCreateSystemServerClassLoader = GetStaticMethodIDOrDie(env, gZygoteInitClass, - "createSystemServerClassLoader", - "()V"); - - RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods)); - - return JNI_OK; + return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods)); } } // namespace android diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 030483bc4c33..ef6eb38043f6 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -724,6 +724,16 @@ enum Action { // CATEGORY: SETTINGS // OS: R ACTION_ADB_WIRELESS_OFF = 1735; + + // ACTION: Change Wi-Fi hotspot name + // CATEGORY: SETTINGS + // OS: R + ACTION_SETTINGS_CHANGE_WIFI_HOTSPOT_NAME = 1736; + + // ACTION: Change Wi-Fi hotspot password + // CATEGORY: SETTINGS + // OS: R + ACTION_SETTINGS_CHANGE_WIFI_HOTSPOT_PASSWORD = 1737; } /** diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto index 4a7d04361a5b..e8ae1b3c9dc5 100644 --- a/core/proto/android/content/package_item_info.proto +++ b/core/proto/android/content/package_item_info.proto @@ -109,6 +109,7 @@ message ApplicationInfoProto { } optional int32 network_security_config_res = 17; optional int32 category = 18; + optional bool enable_gwp_asan = 19; } optional Detail detail = 17; } diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto index 477716982008..f14e3ed1872d 100644 --- a/core/proto/android/telephony/enums.proto +++ b/core/proto/android/telephony/enums.proto @@ -20,6 +20,43 @@ package android.telephony; option java_outer_classname = "TelephonyProtoEnums"; option java_multiple_files = true; +enum CallBearerEnum { + /** Call bearer is unknown or invalid */ + CALL_BEARER_UNKNOWN = 0; + + /** Call bearer is legacy CS */ + CALL_BEARER_CS = 1; + + /** Call bearer is IMS */ + CALL_BEARER_IMS = 2; +} + +enum CallDirectionEnum { + /** Call direction: unknown or invalid */ + CALL_DIRECTION_UNKNOWN = 0; + + /** Call direction: mobile originated (outgoing for this device) */ + CALL_DIRECTION_MO = 1; + + /** Call direction: mobile terminated (incoming for this device) */ + CALL_DIRECTION_MT = 2; +} + +// Call setup duration buckets. +// See com.android.internal.telephony.metrics.VoiceCallSessionStats for definition. +enum CallSetupDurationEnum { + CALL_SETUP_DURATION_UNKNOWN = 0; + CALL_SETUP_DURATION_EXTREMELY_FAST = 1; + CALL_SETUP_DURATION_ULTRA_FAST = 2; + CALL_SETUP_DURATION_VERY_FAST = 3; + CALL_SETUP_DURATION_FAST = 4; + CALL_SETUP_DURATION_NORMAL = 5; + CALL_SETUP_DURATION_SLOW = 6; + CALL_SETUP_DURATION_VERY_SLOW = 7; + CALL_SETUP_DURATION_ULTRA_SLOW = 8; + CALL_SETUP_DURATION_EXTREMELY_SLOW = 9; +} + // Data conn. power states, primarily used by android/telephony/DataConnectionRealTimeInfo.java. enum DataConnectionPowerStateEnum { DATA_CONNECTION_POWER_STATE_LOW = 1; @@ -63,7 +100,6 @@ enum SignalStrengthEnum { SIGNAL_STRENGTH_GREAT = 4; } - enum ServiceStateEnum { /** * Normal operation condition, the phone is registered diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5086c7e98598..4001defe51a9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1187,6 +1187,16 @@ android:description="@string/permdesc_callCompanionApp" android:protectionLevel="normal" /> + <!-- Exempt this uid from restrictions to background audio recoding + <p>Protection level: signature|privileged + @hide + @SystemApi + --> + <permission android:name="android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS" + android:label="@string/permlab_exemptFromAudioRecordRestrictions" + android:description="@string/permdesc_exemptFromAudioRecordRestrictions" + android:protectionLevel="signature|privileged" /> + <!-- Allows a calling app to continue a call which was started in another app. An example is a video calling app that wants to continue a voice call on the user's mobile network.<p> When the handover of a call from one app to another takes place, there are two devices @@ -3735,7 +3745,8 @@ <permission android:name="android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY" android:protectionLevel="signature|installer" /> - <!-- @hide Allows an application to upgrade runtime permissions. --> + <!-- @SystemApi @TestApi Allows an application to upgrade runtime permissions. + @hide --> <permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" android:protectionLevel="signature" /> @@ -4992,15 +5003,15 @@ <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS" android:protectionLevel="signature|appPredictor" /> - <!-- Feature Id for Country Detector. --> - <feature android:featureId="CountryDetector" android:label="@string/country_detector"/> - <!-- Feature Id for Location service. --> - <feature android:featureId="LocationService" android:label="@string/location_service"/> - <!-- Feature Id for Sensor Notification service. --> - <feature android:featureId="SensorNotificationService" + <!-- Attribution for Country Detector. --> + <attribution android:tag="CountryDetector" android:label="@string/country_detector"/> + <!-- Attribution for Location service. --> + <attribution android:tag="LocationService" android:label="@string/location_service"/> + <!-- Attribution for Sensor Notification service. --> + <attribution android:tag="SensorNotificationService" android:label="@string/sensor_notification_service"/> <!-- Feature Id for Twilight service. --> - <feature android:featureId="TwilightService" android:label="@string/twilight_service"/> + <attribution android:tag="TwilightService" android:label="@string/twilight_service"/> <application android:process="system" android:persistent="true" @@ -5189,6 +5200,7 @@ <activity android:name="com.android.internal.app.BlockedAppActivity" android:theme="@style/Theme.Dialog.Confirmation" android:excludeFromRecents="true" + android:lockTaskMode="always" android:process=":ui"> </activity> diff --git a/core/res/res/drawable/conversation_badge_background.xml b/core/res/res/drawable/conversation_badge_background.xml new file mode 100644 index 000000000000..0dd0dcda40fb --- /dev/null +++ b/core/res/res/drawable/conversation_badge_background.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + + <solid + android:color="#ffffff"/> + + <size + android:width="26dp" + android:height="26dp"/> +</shape> + diff --git a/core/res/res/drawable/ic_collapse_notification.xml b/core/res/res/drawable/ic_collapse_notification.xml index 124e99e3a4bb..ca4f0ed27a0b 100644 --- a/core/res/res/drawable/ic_collapse_notification.xml +++ b/core/res/res/drawable/ic_collapse_notification.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -Copyright (C) 2015 The Android Open Source Project +Copyright (C) 2020 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,11 +15,14 @@ Copyright (C) 2015 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="14.0dp" - android:height="14.0dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:width="22.0dp" + android:height="22.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> <path android:fillColor="#FF000000" - android:pathData="M12.0,8.0l-6.0,6.0l1.4,1.4l4.6,-4.6l4.6,4.6L18.0,14.0L12.0,8.0z"/> -</vector> + android:pathData="M18.59,16.41L20.0,15.0l-8.0,-8.0 -8.0,8.0 1.41,1.41L12.0,9.83"/> + <path + android:pathData="M0 0h24v24H0V0z" + android:fillColor="#00000000"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_expand_notification.xml b/core/res/res/drawable/ic_expand_notification.xml index 847e3269398d..a080ce43cfec 100644 --- a/core/res/res/drawable/ic_expand_notification.xml +++ b/core/res/res/drawable/ic_expand_notification.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -Copyright (C) 2015 The Android Open Source Project +Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,11 +15,14 @@ Copyright (C) 2015 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="14.0dp" - android:height="14.0dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:width="22.0dp" + android:height="22.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> <path android:fillColor="#FF000000" - android:pathData="M16.6,8.6L12.0,13.2L7.4,8.6L6.0,10.0l6.0,6.0l6.0,-6.0L16.6,8.6z"/> -</vector> + android:pathData="M5.41,7.59L4.0,9.0l8.0,8.0 8.0,-8.0 -1.41,-1.41L12.0,14.17"/> + <path + android:pathData="M24 24H0V0h24v24z" + android:fillColor="#00000000"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/layout-car/car_resolver_different_item_header.xml b/core/res/res/layout-car/car_resolver_different_item_header.xml deleted file mode 100644 index 222ecc689792..000000000000 --- a/core/res/res/layout-car/car_resolver_different_item_header.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* - * Copyright 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ ---> -<TextView - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_alwaysShow="true" - android:text="@*android:string/use_a_different_app" - android:minHeight="56dp" - android:textAppearance="?android:attr/textAppearanceLarge" - android:gravity="start|center_vertical" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:elevation="8dp" -/>
\ No newline at end of file diff --git a/core/res/res/layout-car/car_resolver_list.xml b/core/res/res/layout-car/car_resolver_list.xml index 15a864505d3f..755cbfe1b419 100644 --- a/core/res/res/layout-car/car_resolver_list.xml +++ b/core/res/res/layout-car/car_resolver_list.xml @@ -23,90 +23,142 @@ android:id="@id/contentPanel"> <LinearLayout - android:id="@+id/button_bar" - android:visibility="gone" - style="?attr/buttonBarStyle" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_ignoreOffset="true" + android:layout_height="match_parent" + android:weightSum="5" android:layout_alwaysShow="true" - android:layout_hasNestedScrollIndicator="true" + android:orientation="vertical" android:background="?attr/colorBackgroundFloating" - android:orientation="horizontal" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:paddingStart="12dp" - android:weightSum="5" - android:paddingEnd="12dp" android:elevation="8dp"> - <TextView - android:id="@+id/profile_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="8dp" - android:paddingStart="8dp" - android:paddingEnd="8dp" - android:textSize="40sp" - android:layout_weight="5" - android:layout_gravity = "left" + <LinearLayout + android:id="@+id/button_bar" android:visibility="gone" - android:textColor="?attr/colorAccent" - android:singleLine="true"/> - - <TextView - android:id="@+id/title" - android:layout_width="wrap_content" + style="?attr/buttonBarStyle" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:minHeight="56dp" - android:layout_gravity = "left" - android:layout_weight="3" + android:layout_ignoreOffset="true" + android:layout_alwaysShow="true" + android:layout_hasNestedScrollIndicator="true" + android:background="?attr/colorBackgroundFloating" + android:orientation="horizontal" android:paddingTop="8dp" - android:layout_below="@id/profile_button" - android:paddingBottom="8dp"/> + android:paddingStart="12dp" + android:weightSum="4" + android:paddingEnd="12dp" + android:elevation="8dp"> + + <TextView + android:id="@+id/profile_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:paddingStart="8dp" + android:paddingEnd="8dp" + android:textSize="40sp" + android:layout_weight="4" + android:layout_gravity="left" + android:visibility="gone" + android:textColor="?attr/colorAccent" + android:singleLine="true"/> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="left" + android:layout_weight="3" + android:paddingTop="8dp" + android:layout_below="@id/profile_button" + android:textAppearance="?android:attr/textAppearanceLarge" + android:paddingBottom="8dp"/> + + <Button + android:id="@+id/button_once" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:enabled="false" + android:layout_gravity="right" + style="?attr/buttonBarButtonStyle" + android:text="@string/activity_resolver_use_once" + android:layout_weight="0.5" + android:onClick="onButtonClick"/> - <Button - android:id="@+id/button_once" - android:layout_width="wrap_content" + <Button + android:id="@+id/button_always" + android:layout_marginLeft="2dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:enabled="false" + android:layout_gravity="right" + style="?attr/buttonBarButtonStyle" + android:text="@string/activity_resolver_use_always" + android:layout_weight="0.5" + android:onClick="onButtonClick"/> + </LinearLayout> + + <FrameLayout + android:id="@+id/stub" + android:visibility="gone" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:enabled="false" - android:layout_gravity = "right" - android:text="@string/activity_resolver_use_once" - android:layout_weight="1" - android:onClick="onButtonClick"/> + android:background="?attr/colorBackgroundFloating"/> - <Button - android:id="@+id/button_always" - android:layout_marginLeft="10dp" - android:layout_width="wrap_content" + <TabHost + android:id="@+id/profile_tabhost" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:enabled="false" - android:layout_gravity = "right" - android:text="@string/activity_resolver_use_always" - android:layout_weight="1" - android:onClick="onButtonClick"/> - </LinearLayout> + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:background="?attr/colorBackgroundFloating"> + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TabWidget + android:id="@android:id/tabs" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone"> + </TabWidget> + <View + android:id="@+id/resolver_tab_divider" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/colorBackgroundFloating" + android:foreground="?attr/dividerVertical" + android:layout_marginBottom="8dp"/> + <FrameLayout + android:id="@android:id/tabcontent" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <com.android.internal.app.ResolverViewPager + android:id="@+id/profile_pager" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + </FrameLayout> + </LinearLayout> + </TabHost> - <ListView - android:layout_width="match_parent" - android:layout_height="500dp" - android:id="@+id/resolver_list" - android:clipToPadding="false" - android:scrollbarStyle="outsideOverlay" - android:background="?attr/colorBackgroundFloating" - android:elevation="8dp" - android:nestedScrollingEnabled="true" - android:scrollIndicators="top|bottom"/> + <View + android:layout_alwaysShow="true" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/colorBackgroundFloating" + android:foreground="?attr/dividerVertical"/> + + <TextView android:id="@+id/empty" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/colorBackgroundFloating" + android:elevation="8dp" + android:layout_alwaysShow="true" + android:text="@string/noApplications" + android:padding="32dp" + android:gravity="center" + android:visibility="gone"/> - <TextView android:id="@+id/empty" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?attr/colorBackgroundFloating" - android:elevation="8dp" - android:layout_alwaysShow="true" - android:text="@string/noApplications" - android:padding="32dp" - android:gravity="center" - android:visibility="gone"/> + </LinearLayout> </com.android.internal.widget.ResolverDrawerLayout> diff --git a/core/res/res/layout-car/car_resolver_list_with_default.xml b/core/res/res/layout-car/car_resolver_list_with_default.xml index 2aed00bea14e..5e450b221959 100644 --- a/core/res/res/layout-car/car_resolver_list_with_default.xml +++ b/core/res/res/layout-car/car_resolver_list_with_default.xml @@ -40,12 +40,12 @@ <ImageView android:id="@+id/icon" - android:layout_width="24dp" - android:layout_height="24dp" + android:layout_width="60dp" + android:layout_height="60dp" android:layout_gravity="start|top" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" - android:layout_marginTop="20dp" + android:layout_marginStart="10dp" + android:layout_marginEnd="5dp" + android:layout_marginTop="10dp" android:src="@drawable/resolver_icon_placeholder" android:scaleType="fitCenter"/> @@ -55,7 +55,7 @@ android:layout_weight="1" android:layout_height="?attr/listPreferredItemHeight" android:layout_marginStart="16dp" - android:textAppearance="?attr/textAppearanceMedium" + android:textAppearance="?android:attr/textAppearanceLarge" android:gravity="start|center_vertical" android:paddingEnd="16dp"/> @@ -120,7 +120,7 @@ android:layout_width="wrap_content" android:layout_gravity="start" android:maxLines="2" - style="?attr/buttonBarNegativeButtonStyle" + style="?attr/buttonBarButtonStyle" android:minHeight="@dimen/alert_dialog_button_bar_height" android:layout_height="wrap_content" android:enabled="false" @@ -133,29 +133,64 @@ android:layout_gravity="end" android:maxLines="2" android:minHeight="@dimen/alert_dialog_button_bar_height" - style="?attr/buttonBarPositiveButtonStyle" + style="?attr/buttonBarButtonStyle" android:layout_height="wrap_content" android:enabled="false" android:text="@string/activity_resolver_use_always" android:onClick="onButtonClick"/> </LinearLayout> - <View + <FrameLayout + android:id="@+id/stub" + android:layout_alwaysShow="true" + android:visibility="gone" android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?attr/dividerVertical"/> + android:layout_height="wrap_content" + android:background="?attr/colorBackgroundFloating"/> - <ListView + <TabHost + android:layout_alwaysShow="true" + android:id="@+id/profile_tabhost" android:layout_width="match_parent" android:layout_height="wrap_content" - android:id="@+id/resolver_list" - android:layout_weight="4" - android:clipToPadding="false" - android:scrollbarStyle="outsideOverlay" - android:background="?attr/colorBackgroundFloating" - android:elevation="8dp" - android:nestedScrollingEnabled="true" - android:divider="@null"/> + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:background="?attr/colorBackgroundFloating"> + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TabWidget + android:id="@android:id/tabs" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone"> + </TabWidget> + <View + android:id="@+id/resolver_tab_divider" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/colorBackgroundFloating" + android:foreground="?attr/dividerVertical" + android:layout_marginBottom="8dp"/> + <FrameLayout + android:id="@android:id/tabcontent" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <com.android.internal.app.ResolverViewPager + android:id="@+id/profile_pager" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + </com.android.internal.app.ResolverViewPager> + </FrameLayout> + </LinearLayout> + </TabHost> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/dividerVertical"/> </LinearLayout> </com.android.internal.widget.ResolverDrawerLayout> diff --git a/core/res/res/layout/conversation_face_pile_layout.xml b/core/res/res/layout/conversation_face_pile_layout.xml new file mode 100644 index 000000000000..1db38702f926 --- /dev/null +++ b/core/res/res/layout/conversation_face_pile_layout.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/conversation_face_pile" + android:layout_width="@dimen/conversation_avatar_size" + android:layout_height="@dimen/conversation_avatar_size" + android:forceHasOverlappingRendering="false" + > + <ImageView + android:id="@+id/conversation_face_pile_top" + android:layout_width="36dp" + android:layout_height="36dp" + android:scaleType="centerCrop" + android:layout_gravity="end|top" + /> + <FrameLayout + android:id="@+id/conversation_face_pile_bottom_background" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_gravity="start|bottom" + android:background="@drawable/conversation_badge_background"> + <ImageView + android:id="@+id/conversation_face_pile_bottom" + android:layout_width="36dp" + android:layout_height="36dp" + android:scaleType="centerCrop" + android:layout_gravity="center" + /> + </FrameLayout> +</FrameLayout> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index f5fa1b6a795a..6f36aae8a1d4 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -91,7 +91,6 @@ android:textAppearance="@style/TextAppearance.Material.Notification.Time" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" android:layout_marginStart="@dimen/notification_header_separating_margin" android:layout_marginEnd="@dimen/notification_header_separating_margin" android:showRelative="true" diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml new file mode 100644 index 000000000000..dc52e979a310 --- /dev/null +++ b/core/res/res/layout/notification_template_material_conversation.xml @@ -0,0 +1,205 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<com.android.internal.widget.ConversationLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:tag="conversation" + android:theme="@style/Theme.DeviceDefault.Notification" + > + + <FrameLayout + android:layout_width="@dimen/conversation_content_start" + android:layout_height="wrap_content" + android:gravity="start|top" + android:clipChildren="false" + android:clipToPadding="false" + android:paddingTop="12dp" + > + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|center_horizontal" + > + + <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right --> + <ImageView + android:id="@+id/conversation_icon" + android:layout_width="@dimen/conversation_avatar_size" + android:layout_height="@dimen/conversation_avatar_size" + android:scaleType="centerCrop" + android:importantForAccessibility="no" + /> + + <ViewStub + android:layout="@layout/conversation_face_pile_layout" + android:layout_width="@dimen/conversation_avatar_size" + android:layout_height="@dimen/conversation_avatar_size" + android:id="@+id/conversation_face_pile" + /> + + <FrameLayout + android:id="@+id/conversation_icon_badge" + android:layout_width="20dp" + android:layout_height="20dp" + android:layout_marginLeft="@dimen/conversation_badge_side_margin" + android:layout_marginTop="@dimen/conversation_badge_side_margin" + android:background="@drawable/conversation_badge_background" > + <!-- Badge: 20x20, 48dp padding left + top --> + <com.android.internal.widget.CachingIconView + android:id="@+id/icon" + android:layout_width="@dimen/conversation_icon_size_badged" + android:layout_height="@dimen/conversation_icon_size_badged" + android:layout_gravity="center" + /> + </FrameLayout> + </FrameLayout> + </FrameLayout> + + <!-- Wraps entire "expandable" notification --> + <com.android.internal.widget.RemeasuringLinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:clipToPadding="false" + android:clipChildren="false" + android:orientation="vertical" + > + <!-- LinearLayout for Expand Button--> + <com.android.internal.widget.RemeasuringLinearLayout + android:id="@+id/expand_button_and_content_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="start|top" + android:orientation="horizontal" + android:clipChildren="false" + android:clipToPadding="false"> + <!--TODO: move this into a separate layout and share logic with the header to bring back app opps etc--> + <FrameLayout + android:id="@+id/notification_action_list_margin_target" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1"> + + <!-- Header --> + <LinearLayout + android:id="@+id/conversation_header" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingTop="16dp" + android:paddingStart="@dimen/conversation_content_start" + > + <TextView + android:id="@+id/conversation_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_header_separating_margin" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" + android:textSize="16sp" + android:singleLine="true" + /> + + <TextView + android:id="@+id/time_divider" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?attr/notificationHeaderTextAppearance" + android:layout_marginStart="@dimen/notification_header_separating_margin" + android:layout_marginEnd="@dimen/notification_header_separating_margin" + android:text="@string/notification_header_divider_symbol" + android:layout_gravity="center" + android:paddingTop="1sp" + android:singleLine="true" + android:visibility="gone" + /> + + <DateTimeView + android:id="@+id/time" + android:textAppearance="@style/TextAppearance.Material.Notification.Time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginStart="@dimen/notification_header_separating_margin" + android:paddingTop="1sp" + android:showRelative="true" + android:singleLine="true" + android:visibility="gone" + /> + + <ImageView + android:id="@+id/profile_badge" + android:layout_width="@dimen/notification_badge_size" + android:layout_height="@dimen/notification_badge_size" + android:layout_gravity="center" + android:layout_marginStart="4dp" + android:paddingTop="2dp" + android:scaleType="fitCenter" + android:visibility="gone" + android:contentDescription="@string/notification_work_profile_content_description" + /> + </LinearLayout> + + <!-- Messages --> + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/notification_messaging" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="40dp" + android:spacing="@dimen/notification_messaging_spacing" + android:clipToPadding="false" + android:clipChildren="false" + /> + </FrameLayout> + <!-- Unread Count --> + <!-- <TextView /> --> + + <!-- This is where the expand button will be placed when collapsed--> + </com.android.internal.widget.RemeasuringLinearLayout> + + <include layout="@layout/notification_template_smart_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_content_margin" + android:layout_marginStart="@dimen/conversation_content_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" /> + <include layout="@layout/notification_material_action_list" /> + </com.android.internal.widget.RemeasuringLinearLayout> + + <!--This is dynamically placed between here and at the end of the layout--> + <FrameLayout + android:id="@+id/expand_button_container" + android:layout_width="wrap_content" + android:layout_height="@dimen/conversation_expand_button_expanded_size" + android:layout_gravity="end|top" + android:paddingStart="16dp" + android:paddingEnd="@dimen/notification_content_margin_end"> + <com.android.internal.widget.NotificationExpandButton + android:id="@+id/expand_button" + android:layout_width="@dimen/notification_header_expand_icon_size" + android:layout_height="@dimen/notification_header_expand_icon_size" + android:layout_gravity="center" + android:drawable="@drawable/ic_expand_notification" + android:clickable="false" + android:importantForAccessibility="no" + /> + </FrameLayout> +</com.android.internal.widget.ConversationLayout> diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml index 483b479538a1..15146c073e46 100644 --- a/core/res/res/layout/notification_template_messaging_group.xml +++ b/core/res/res/layout/notification_template_messaging_group.xml @@ -20,14 +20,19 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > - <ImageView - android:id="@+id/message_icon" - android:layout_width="@dimen/messaging_avatar_size" - android:layout_height="@dimen/messaging_avatar_size" - android:layout_marginEnd="12dp" - android:scaleType="centerCrop" - android:importantForAccessibility="no" /> + <FrameLayout + android:layout_width="@dimen/conversation_content_start" + android:layout_height="wrap_content"> <!--TODO: make sure to make this padding dynamic--> + <ImageView + android:layout_gravity="top|center_horizontal" + android:id="@+id/message_icon" + android:layout_width="@dimen/messaging_avatar_size" + android:layout_height="@dimen/messaging_avatar_size" + android:scaleType="centerCrop" + android:importantForAccessibility="no" /> + </FrameLayout> <com.android.internal.widget.RemeasuringLinearLayout + android:id="@+id/messaging_group_content_container" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" @@ -43,7 +48,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/notification_text_margin_top" - android:spacing="2dp"/> + android:spacing="2dp" /> </com.android.internal.widget.RemeasuringLinearLayout> <FrameLayout android:id="@+id/messaging_group_icon_container" diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml index e17fc8a16b9f..b754e0cfc022 100644 --- a/core/res/res/layout/resolver_list.xml +++ b/core/res/res/layout/resolver_list.xml @@ -100,8 +100,7 @@ android:layout_width="match_parent" android:layout_height="1dp" android:background="?attr/colorBackgroundFloating" - android:foreground="?attr/dividerVertical" - android:layout_marginBottom="@dimen/resolver_tab_divider_bottom_padding"/> + android:foreground="?attr/dividerVertical"/> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml index 0d4523add3cb..06a7633ce3f5 100644 --- a/core/res/res/layout/resolver_list_with_default.xml +++ b/core/res/res/layout/resolver_list_with_default.xml @@ -183,8 +183,7 @@ android:layout_width="match_parent" android:layout_height="1dp" android:background="?attr/colorBackgroundFloating" - android:foreground="?attr/dividerVertical" - android:layout_marginBottom="@dimen/resolver_tab_divider_bottom_padding"/> + android:foreground="?attr/dividerVertical"/> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" diff --git a/core/res/res/values-mcc219/config.xml b/core/res/res/values-mcc219/config.xml deleted file mode 100644 index 7ae82fa91c95..000000000000 --- a/core/res/res/values-mcc219/config.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- These resources are around just to allow their values to be customized - for different hardware and product builds. --> -<resources> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - <string-array name="config_twoDigitNumberPattern"> - <item>"92"</item> - <item>"93"</item> - <item>"94"</item> - <item>"95"</item> - <item>"96"</item> - </string-array> - -</resources> diff --git a/core/res/res/values-mcc220/config.xml b/core/res/res/values-mcc220/config.xml deleted file mode 100644 index 7ae82fa91c95..000000000000 --- a/core/res/res/values-mcc220/config.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- These resources are around just to allow their values to be customized - for different hardware and product builds. --> -<resources> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - <string-array name="config_twoDigitNumberPattern"> - <item>"92"</item> - <item>"93"</item> - <item>"94"</item> - <item>"95"</item> - <item>"96"</item> - </string-array> - -</resources> diff --git a/core/res/res/values-mcc310-mnc150/config.xml b/core/res/res/values-mcc310-mnc150/config.xml deleted file mode 100644 index e7d1325ff78a..000000000000 --- a/core/res/res/values-mcc310-mnc150/config.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string-array translatable="false" name="config_twoDigitNumberPattern"> - <item>"0"</item> - <item>"00"</item> - <item>"*0"</item> - <item>"*1"</item> - <item>"*2"</item> - <item>"*3"</item> - <item>"*4"</item> - <item>"*5"</item> - <item>"*6"</item> - <item>"*7"</item> - <item>"*8"</item> - <item>"*9"</item> - <item>"#0"</item> - <item>"#1"</item> - <item>"#2"</item> - <item>"#3"</item> - <item>"#4"</item> - <item>"#5"</item> - <item>"#6"</item> - <item>"#7"</item> - <item>"#8"</item> - <item>"#9"</item> - </string-array> -</resources> diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml index 3fb3f0f7e9ff..53e4193c7f38 100644 --- a/core/res/res/values-mcc310-mnc410/config.xml +++ b/core/res/res/values-mcc310-mnc410/config.xml @@ -23,31 +23,6 @@ <!-- Configure mobile network MTU. Carrier specific value is set here. --> <integer name="config_mobile_mtu">1410</integer> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - <string-array name="config_twoDigitNumberPattern"> - <item>"0"</item> - <item>"00"</item> - <item>"*0"</item> - <item>"*1"</item> - <item>"*2"</item> - <item>"*3"</item> - <item>"*4"</item> - <item>"*5"</item> - <item>"*6"</item> - <item>"*7"</item> - <item>"*8"</item> - <item>"*9"</item> - <item>"#0"</item> - <item>"#1"</item> - <item>"#2"</item> - <item>"#3"</item> - <item>"#4"</item> - <item>"#5"</item> - <item>"#6"</item> - <item>"#7"</item> - <item>"#8"</item> - <item>"#9"</item> - </string-array> <!-- Enable 5 bar signal strength icon --> <bool name="config_inflateSignalStrength">true</bool> diff --git a/core/res/res/values-mcc313-mnc100/config.xml b/core/res/res/values-mcc313-mnc100/config.xml deleted file mode 100644 index ccd03f10616a..000000000000 --- a/core/res/res/values-mcc313-mnc100/config.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2019, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string-array translatable="false" name="config_twoDigitNumberPattern"> - <item>"0"</item> - <item>"00"</item> - <item>"*0"</item> - <item>"*1"</item> - <item>"*2"</item> - <item>"*3"</item> - <item>"*4"</item> - <item>"*5"</item> - <item>"*6"</item> - <item>"*7"</item> - <item>"*8"</item> - <item>"*9"</item> - <item>"#0"</item> - <item>"#1"</item> - <item>"#2"</item> - <item>"#3"</item> - <item>"#4"</item> - <item>"#5"</item> - <item>"#6"</item> - <item>"#7"</item> - <item>"#8"</item> - <item>"#9"</item> - </string-array> -</resources> diff --git a/core/res/res/values-mcc334-mnc050/config.xml b/core/res/res/values-mcc334-mnc050/config.xml index 23678f15fba3..2e8c504f7d8e 100644 --- a/core/res/res/values-mcc334-mnc050/config.xml +++ b/core/res/res/values-mcc334-mnc050/config.xml @@ -31,8 +31,4 @@ <item>9</item> </integer-array> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - <string-array translatable="false" name="config_twoDigitNumberPattern"> - <item>"#9"</item> - </string-array> </resources> diff --git a/core/res/res/values-mcc334-mnc090/config.xml b/core/res/res/values-mcc334-mnc090/config.xml deleted file mode 100644 index 1632a4239923..000000000000 --- a/core/res/res/values-mcc334-mnc090/config.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2017, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You my obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- These resources are around just to allow their values to be customized - for different hardware and product builds. --> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - - <string-array translatable="false" name="config_twoDigitNumberPattern"> - <item>"#9"</item> - </string-array> -</resources> diff --git a/core/res/res/values-mcc704-mnc01/config.xml b/core/res/res/values-mcc704-mnc01/config.xml deleted file mode 100644 index 10b647044c73..000000000000 --- a/core/res/res/values-mcc704-mnc01/config.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2016, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You my obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<resources> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - <string-array name="config_twoDigitNumberPattern"> - <item>"*1"</item> - <item>"*5"</item> - <item>"*9"</item> - </string-array> -</resources> diff --git a/core/res/res/values-mcc708-mnc001/config.xml b/core/res/res/values-mcc708-mnc001/config.xml deleted file mode 100755 index 7b7c48d46b14..000000000000 --- a/core/res/res/values-mcc708-mnc001/config.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2016, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You my obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- These resources are around just to allow their values to be customized - for different hardware and product builds. --> -<resources> - <string-array translatable="false" name="config_twoDigitNumberPattern"> - <item>"*1"</item> - <item>"*5"</item> - </string-array> -</resources> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index c2451319bdc8..cb88aee33bfc 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1541,6 +1541,28 @@ <flag name="microphone" value="0x80" /> </attr> + <!-- Enable sampled memory bug detection in this process. + When enabled, a very small, random subset of native + memory allocations are protected with guard pages, providing an + ASan-like error report in case of a memory corruption bug. + + GWP-ASan is a recursive acronym. It stands for “GWP-ASan Will Provide Allocation SANity”. + See the <a href="http://llvm.org/docs/GwpAsan.html">LLVM documentation</a> + for more information about this feature. + + <p>This tag can be applied to any tag that allows + {@link android.R.styleable#AndroidManifestProcess process} tag: + {@link android.R.styleable#AndroidManifestApplication application} tag (to supply + a default setting for all application components), or with the + {@link android.R.styleable#AndroidManifestProvider provider}, + {@link android.R.styleable#AndroidManifestService service}, + {@link android.R.styleable#AndroidManifestReceiver receiver}, + {@link android.R.styleable#AndroidManifestActivity activity} tag. + When multiple components run in the same process, + the first component to start determines the behavior of the entire process. + + The default value is {@code false}. --> + <attr name="enableGwpAsan" format="boolean" /> <!-- The <code>manifest</code> tag is the root of an <code>AndroidManifest.xml</code> file, @@ -1552,7 +1574,7 @@ <code>com.google.app.<em>appname</em></code> <p>Inside of the manifest tag, may appear the following tags - in any order: {@link #AndroidManifestFeature feature}, + in any order: {@link #AndroidManifestAttribution attribution}, {@link #AndroidManifestPermission permission}, {@link #AndroidManifestPermissionGroup permission-group}, {@link #AndroidManifestPermissionTree permission-tree}, @@ -1806,31 +1828,38 @@ The default value is {@code true}. --> <attr name="allowNativeHeapPointerTagging" format="boolean" /> + + <attr name="enableGwpAsan" /> + </declare-styleable> - <!-- The <code>feature</code> tag declares a feature. A feature is a logical part of an app. - E.g. photo sharing app might include a direct messaging component. To tag certain code as - belonging to a feature, use a context created via - {@link android.content.Context#createFeatureContext(String)} for any interaction with the + <!-- An attribution is a logical part of an app and is identified by a tag. + E.g. a photo sharing app might include a direct messaging component. To tag certain code as + belonging to an attribution, use a context created via + {@link android.content.Context#createAttributionContext(String)} for any interaction with the system. <p>This appears as a child tag of the root {@link #AndroidManifest manifest} tag. - <p>In case this feature inherits from another feature, this tag can contain one or multiple - {@link #AndroidManifestFeatureInheritFrom inherit-from} tags. --> - <declare-styleable name="AndroidManifestFeature" parent="AndroidManifest"> - <!-- Required identifier for a feature. Can be passed to - {@link android.content.Context#createFeatureContext} to create a context for this feature - --> + <p>In case this attribution inherits from another attribution, this tag can contain one or + multiple {@link #AndroidManifestAttributionInheritFrom inherit-from} tags. --> + <declare-styleable name="AndroidManifestAttribution" parent="AndroidManifest"> + <!-- TODO moltmann: Remove --> <attr name="featureId" format="string" /> - <!-- Required user visible label for a feature. --> + <!-- Required identifier for a attribution. Can be passed to + {@link android.content.Context#createAttributionContext} to create a context tagged with + this attribution + --> + <attr name="tag" format="string" /> + <!-- Required user visible label for a attribution. --> <attr name="label" format="string" /> </declare-styleable> <!-- Declares previously declared features this feature inherits from. --> - <declare-styleable name="AndroidManifestFeatureInheritFrom" parent="AndroidManifestFeature"> - <!-- Identifier of the feature this feature inherits from --> - <attr name="featureId" format="string" /> + <declare-styleable name="AndroidManifestAttributionInheritFrom" + parent="AndroidManifestAttribution"> + <!-- Identifier of the attribution this attribution inherits from --> + <attr name="tag" format="string" /> </declare-styleable> <!-- The <code>permission</code> tag declares a security permission that can be @@ -2312,6 +2341,7 @@ <declare-styleable name="AndroidManifestProcess" parent="AndroidManifestProcesses"> <!-- Required name of the process that is allowed --> <attr name="process" /> + <attr name="enableGwpAsan" /> </declare-styleable> <!-- The <code>deny-permission</code> tag specifies that a permission is to be denied diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0b6e65fe9407..6f468e03e0b7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2024,7 +2024,10 @@ a keyboard is present. --> <bool name="config_showMenuShortcutsWhenKeyboardPresent">false</bool> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> + <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD. + + Note: This config is deprecated, please use carrier config which is + CarrierConfigManager.KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY instead. --> <string-array name="config_twoDigitNumberPattern" translatable="false"> </string-array> @@ -4415,4 +4418,7 @@ <!-- Package name of custom session policy provider class used by MediaSessionService. --> <string name="config_customSessionPolicyProvider"></string> + + <!-- The max scale for the wallpaper when it's zoomed in --> + <item name="config_wallpaperMaxScale" format="float" type="dimen">1</item> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 91186179c6d8..e3fe982c226d 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -682,7 +682,25 @@ <!-- The size of the right icon image when on low ram --> <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen> - <dimen name="messaging_avatar_size">52dp</dimen> + <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen> + <dimen name="conversation_avatar_size">52dp</dimen> + <!-- Start of the content in the conversation template --> + <dimen name="conversation_content_start">80dp</dimen> + <!-- Size of the expand button when expanded --> + <dimen name="conversation_expand_button_expanded_size">80dp</dimen> + <!-- Top margin of the expand button for conversations when expanded --> + <dimen name="conversation_expand_button_top_margin_expanded">18dp</dimen> + <!-- Side margins of the conversation badge in relation to the conversation icon --> + <dimen name="conversation_badge_side_margin">36dp</dimen> + <!-- size of the notification icon when badged in a conversation --> + <dimen name="conversation_icon_size_badged">15dp</dimen> + <!-- size of the notification icon when centered in a conversation --> + <dimen name="conversation_icon_size_centered">20dp</dimen> + <!-- margin on the top when the icon is centered for group conversations --> + <dimen name="conversation_icon_margin_top_centered">5dp</dimen> + + <!-- Padding between text and sender when singleline --> + <dimen name="messaging_group_singleline_sender_padding_end">4dp</dimen> <dimen name="messaging_group_sending_progress_size">24dp</dimen> @@ -776,7 +794,6 @@ <dimen name="resolver_empty_state_height_with_tabs">268dp</dimen> <dimen name="resolver_max_collapsed_height">192dp</dimen> <dimen name="resolver_max_collapsed_height_with_tabs">248dp</dimen> - <dimen name="resolver_tab_divider_bottom_padding">8dp</dimen> <dimen name="chooser_action_button_icon_size">18dp</dimen> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 39e81975fce6..96807ccc2b22 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3004,6 +3004,7 @@ <public name="animatedImageDrawable"/> <public name="htmlDescription"/> <public name="preferMinimalPostProcessing"/> + <!-- @removed --> <public name="featureId" /> <public name="supportsInlineSuggestions" /> <public name="crossProfile" /> @@ -3015,6 +3016,7 @@ <public name="allowNativeHeapPointerTagging" /> <public name="preserveLegacyExternalStorage" /> <public name="mimeGroup" /> + <public name="enableGwpAsan" /> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4b49dd340584..f101f590cab1 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1217,6 +1217,14 @@ device. This includes information such as call numbers for calls and the state of the calls.</string> + <!-- Title of an application permission. When granted the app is exempt from audio record + restrictions. + [CHAR LIMIT=NONE]--> + <string name="permlab_exemptFromAudioRecordRestrictions">exempt from audio record restrictions</string> + <!-- Description of an application permission. When granted the app is exempt from audio record + restrictions. [CHAR LIMIT=NONE]--> + <string name="permdesc_exemptFromAudioRecordRestrictions">Exempt the app from restrictions to record audio.</string> + <!-- Title of an application permission. When granted the user is giving access to a third party app to continue a call which originated in another app. For example, the user could be in a voice call over their carrier's mobile network, and a third party video @@ -5432,6 +5440,15 @@ <string name="as_app_forced_to_restricted_bucket"> <xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string> + <!-- The way a conversation name is displayed when single line. The text will be displayed to the end of this text with some spacing --> + <string name="conversation_single_line_name_display"><xliff:g id="sender_name" example="Sara">%1$s</xliff:g>:</string> + + <!-- Conversation Title fallback if the there is no name provided in a 1:1 conversation [CHAR LIMIT=40]--> + <string name="conversation_title_fallback_one_to_one">Conversation</string> + + <!-- Conversation Title fallback if the there is no name provided in a group chat conversation [CHAR LIMIT=40]--> + <string name="conversation_title_fallback_group_chat">Group Conversation</string> + <!-- ResolverActivity - profile tabs --> <!-- Label of a tab on a screen. A user can tap this tap to switch to the 'Personal' view (that shows their personal content) if they have a work profile on their device. [CHAR LIMIT=NONE] --> <string name="resolver_personal_tab">Personal</string> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 966f495c96e5..64768cf4c730 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -146,7 +146,7 @@ easier. <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item> </style> <style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName"> - <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.MessagingName</item> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item> </style> <style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/> <style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/> @@ -290,9 +290,6 @@ easier. <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title"> <item name="fontFamily">@string/config_headlineFontFamilyMedium</item> </style> - <style name="TextAppearance.DeviceDefault.Notification.MessagingName" parent="TextAppearance.DeviceDefault.Notification.Title"> - <item name="textSize">16sp</item> - </style> <style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply"> <item name="fontFamily">@string/config_bodyFontFamily</item> </style> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 63ac0e6bfc3e..2415837cf826 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -504,7 +504,7 @@ please see styles_device_defaults.xml. <style name="Widget.Material.Notification.MessagingText" parent="Widget.Material.Notification.Text"> <item name="layout_width">wrap_content</item> <item name="layout_height">wrap_content</item> - <item name="ellipsize">end</item>z + <item name="ellipsize">end</item> </style> <style name="Widget.Material.Notification.MessagingName" parent="Widget.Material.Light.TextView"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 826379d69dd8..49a0f17aad1a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3876,6 +3876,30 @@ <java-symbol type="array" name="config_defaultImperceptibleKillingExemptionPkgs" /> <java-symbol type="array" name="config_defaultImperceptibleKillingExemptionProcStates" /> + <java-symbol type="string" name="conversation_single_line_name_display" /> + <java-symbol type="string" name="conversation_title_fallback_one_to_one" /> + <java-symbol type="string" name="conversation_title_fallback_group_chat" /> + <java-symbol type="id" name="conversation_icon" /> + <java-symbol type="id" name="conversation_icon_badge" /> + <java-symbol type="id" name="expand_button_container" /> + <java-symbol type="id" name="messaging_group_content_container" /> + <java-symbol type="id" name="expand_button_and_content_container" /> + <java-symbol type="id" name="conversation_header" /> + <java-symbol type="id" name="conversation_face_pile_bottom_background" /> + <java-symbol type="id" name="conversation_face_pile_bottom" /> + <java-symbol type="id" name="conversation_face_pile_top" /> + <java-symbol type="id" name="conversation_face_pile" /> + <java-symbol type="id" name="conversation_text" /> + <java-symbol type="dimen" name="conversation_expand_button_top_margin_expanded" /> + <java-symbol type="dimen" name="conversation_expand_button_expanded_size" /> + <java-symbol type="dimen" name="messaging_group_singleline_sender_padding_end" /> + <java-symbol type="dimen" name="conversation_badge_side_margin" /> + <java-symbol type="dimen" name="conversation_icon_size_badged" /> + <java-symbol type="dimen" name="conversation_icon_size_centered" /> + <java-symbol type="dimen" name="conversation_icon_margin_top_centered" /> + <java-symbol type="layout" name="notification_template_material_conversation" /> + <java-symbol type="layout" name="conversation_face_pile_layout" /> + <!-- Intent resolver and share sheet --> <java-symbol type="color" name="resolver_tabs_active_color" /> <java-symbol type="color" name="resolver_tabs_inactive_color" /> @@ -3909,7 +3933,6 @@ <java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" /> <java-symbol type="dimen" name="resolver_max_collapsed_height_with_tabs" /> <java-symbol type="bool" name="sharesheet_show_content_preview" /> - <java-symbol type="dimen" name="resolver_tab_divider_bottom_padding" /> <!-- Toast message for background started foreground service while-in-use permission restriction feature --> <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" /> @@ -3927,4 +3950,6 @@ <java-symbol type="string" name="config_customMediaKeyDispatcher" /> <java-symbol type="string" name="config_customSessionPolicyProvider" /> + <!-- The max scale for the wallpaper when it's zoomed in --> + <java-symbol type="dimen" name="config_wallpaperMaxScale"/> </resources> diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index 50cd5a3b01b5..fe25e79df44e 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -123,7 +123,7 @@ public class InsetsAnimationControlImplTest { mController = new InsetsAnimationControlImpl(controls, new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(), mMockController, 10 /* durationMs */, new LinearInterpolator(), - false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN, 0 /* animationType */); + false /* fade */, 0 /* animationType */); } @Test @@ -182,7 +182,7 @@ public class InsetsAnimationControlImplTest { @Test public void testCancelled() { - mController.onCancelled(); + mController.cancel(); try { mController.setInsetsAndAlpha(Insets.NONE, 1f /*alpha */, 0f /* fraction */); fail("Expected exception to be thrown"); diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 7737b1a2a776..f1ab8ddebce6 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -31,6 +31,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -47,8 +48,10 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.CancellationSignal; import android.platform.test.annotations.Presubmit; +import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type; +import android.view.WindowInsetsController.OnControllableInsetsChangedListener; import android.view.WindowManager.BadTokenException; import android.view.WindowManager.LayoutParams; import android.view.animation.LinearInterpolator; @@ -67,6 +70,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mockito; import java.util.concurrent.CountDownLatch; import java.util.function.Supplier; @@ -166,28 +171,30 @@ public class InsetsControllerTest { @Test public void testControlsChanged() { - InsetsSourceControl control = - new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); - mController.onControlsChanged(new InsetsSourceControl[] { control }); - assertEquals(mLeash, - mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash()); + mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); + assertNotNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash()); + mController.addOnControllableInsetsChangedListener( + ((controller, typeMask) -> assertEquals(statusBars(), typeMask))); } @Test public void testControlsRevoked() { - InsetsSourceControl control = - new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); - mController.onControlsChanged(new InsetsSourceControl[] { control }); + OnControllableInsetsChangedListener listener + = mock(OnControllableInsetsChangedListener.class); + mController.addOnControllableInsetsChangedListener(listener); + mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); mController.onControlsChanged(new InsetsSourceControl[0]); assertNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl()); + InOrder inOrder = Mockito.inOrder(listener); + inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0)); + inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(statusBars())); + inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0)); } @Test public void testControlsRevoked_duringAnim() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - InsetsSourceControl control = - new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); - mController.onControlsChanged(new InsetsSourceControl[] { control }); + mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); WindowInsetsAnimationControlListener mockListener = mock(WindowInsetsAnimationControlListener.class); @@ -206,10 +213,15 @@ public class InsetsControllerTest { public void testFrameDoesntMatchDisplay() { mController.onFrameChanged(new Rect(0, 0, 100, 100)); mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200)); + InsetsSourceControl control = + new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); + mController.onControlsChanged(new InsetsSourceControl[] { control }); WindowInsetsAnimationControlListener controlListener = mock(WindowInsetsAnimationControlListener.class); mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(), controlListener); + mController.addOnControllableInsetsChangedListener( + (controller, typeMask) -> assertEquals(0, typeMask)); verify(controlListener).onCancelled(); verify(controlListener, never()).onReady(any(), anyInt()); } @@ -245,11 +257,8 @@ public class InsetsControllerTest { @Test public void testApplyImeVisibility() { - final InsetsSourceControl ime = new InsetsSourceControl(ITYPE_IME, mLeash, new Point()); - - InsetsSourceControl[] controls = new InsetsSourceControl[3]; - controls[0] = ime; - mController.onControlsChanged(controls); + InsetsSourceControl ime = createControl(ITYPE_IME); + mController.onControlsChanged(new InsetsSourceControl[] { ime }); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(); mController.applyImeVisibility(true); @@ -412,9 +421,7 @@ public class InsetsControllerTest { @Test public void testRestoreStartsAnimation() { - InsetsSourceControl control = - new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); - mController.onControlsChanged(new InsetsSourceControl[]{control}); + mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mController.hide(Type.statusBars()); @@ -431,7 +438,7 @@ public class InsetsControllerTest { assertTrue(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible()); // Gaining control - mController.onControlsChanged(new InsetsSourceControl[]{control}); + mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR)); mController.cancelExistingAnimation(); assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible()); @@ -442,8 +449,6 @@ public class InsetsControllerTest { @Test public void testStartImeAnimationAfterGettingControl() { - InsetsSourceControl control = - new InsetsSourceControl(ITYPE_IME, mLeash, new Point()); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { @@ -454,7 +459,7 @@ public class InsetsControllerTest { mController.show(ime(), true /* fromIme */); // Gaining control shortly after - mController.onControlsChanged(new InsetsSourceControl[]{control}); + mController.onControlsChanged(createSingletonControl(ITYPE_IME)); assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME)); mController.cancelExistingAnimation(); @@ -466,16 +471,13 @@ public class InsetsControllerTest { @Test public void testStartImeAnimationAfterGettingControl_imeLater() { - InsetsSourceControl control = - new InsetsSourceControl(ITYPE_IME, mLeash, new Point()); - InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mController.show(ime()); assertFalse(mController.getState().getSource(ITYPE_IME).isVisible()); // Gaining control shortly after - mController.onControlsChanged(new InsetsSourceControl[]{control}); + mController.onControlsChanged(createSingletonControl(ITYPE_IME)); // Pretend IME is calling mController.show(ime(), true /* fromIme */); @@ -490,9 +492,7 @@ public class InsetsControllerTest { @Test public void testAnimationEndState_controller() throws Exception { - InsetsSourceControl control = - new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); - mController.onControlsChanged(new InsetsSourceControl[] { control }); + mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { WindowInsetsAnimationControlListener mockListener = @@ -518,9 +518,7 @@ public class InsetsControllerTest { @Test public void testCancellation_afterGainingControl() throws Exception { - InsetsSourceControl control = - new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); - mController.onControlsChanged(new InsetsSourceControl[] { control }); + mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { WindowInsetsAnimationControlListener mockListener = @@ -638,12 +636,23 @@ public class InsetsControllerTest { latch.await(); } + private InsetsSourceControl createControl(@InternalInsetsType int type) { + + // Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will + // attempt to release mLeash directly. + SurfaceControl copy = new SurfaceControl(); + copy.copyFrom(mLeash); + return new InsetsSourceControl(type, copy, new Point()); + } + + private InsetsSourceControl[] createSingletonControl(@InternalInsetsType int type) { + return new InsetsSourceControl[] { createControl(type) }; + } + private InsetsSourceControl[] prepareControls() { - final InsetsSourceControl navBar = new InsetsSourceControl(ITYPE_NAVIGATION_BAR, mLeash, - new Point()); - final InsetsSourceControl statusBar = new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, - new Point()); - final InsetsSourceControl ime = new InsetsSourceControl(ITYPE_IME, mLeash, new Point()); + final InsetsSourceControl navBar = createControl(ITYPE_NAVIGATION_BAR); + final InsetsSourceControl statusBar = createControl(ITYPE_STATUS_BAR); + final InsetsSourceControl ime = createControl(ITYPE_IME); InsetsSourceControl[] controls = new InsetsSourceControl[3]; controls[0] = navBar; diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java index 9787b7780702..9797178fca6e 100644 --- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java @@ -25,12 +25,14 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.os.CancellationSignal; import android.platform.test.annotations.Presubmit; +import android.view.WindowInsetsController.OnControllableInsetsChangedListener; import android.view.animation.LinearInterpolator; import org.junit.Before; @@ -163,11 +165,43 @@ public class PendingInsetsControllerTest { } @Test + public void testAddOnControllableInsetsChangedListener() { + OnControllableInsetsChangedListener listener = + mock(OnControllableInsetsChangedListener.class); + mPendingInsetsController.addOnControllableInsetsChangedListener(listener); + mPendingInsetsController.replayAndAttach(mReplayedController); + verify(mReplayedController).addOnControllableInsetsChangedListener(eq(listener)); + verify(listener).onControllableInsetsChanged(eq(mPendingInsetsController), eq(0)); + } + + @Test + public void testAddRemoveControllableInsetsChangedListener() { + OnControllableInsetsChangedListener listener = + mock(OnControllableInsetsChangedListener.class); + mPendingInsetsController.addOnControllableInsetsChangedListener(listener); + mPendingInsetsController.removeOnControllableInsetsChangedListener(listener); + mPendingInsetsController.replayAndAttach(mReplayedController); + verify(mReplayedController, never()).addOnControllableInsetsChangedListener(any()); + verify(listener).onControllableInsetsChanged(eq(mPendingInsetsController), eq(0)); + } + + @Test + public void testAddOnControllableInsetsChangedListener_direct() { + mPendingInsetsController.replayAndAttach(mReplayedController); + OnControllableInsetsChangedListener listener = + mock(OnControllableInsetsChangedListener.class); + mPendingInsetsController.addOnControllableInsetsChangedListener(listener); + verify(mReplayedController).addOnControllableInsetsChangedListener(eq(listener)); + } + + @Test public void testReplayTwice() { mPendingInsetsController.show(systemBars()); mPendingInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); mPendingInsetsController.setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS); + mPendingInsetsController.addOnControllableInsetsChangedListener( + (controller, typeMask) -> {}); mPendingInsetsController.replayAndAttach(mReplayedController); InsetsController secondController = mock(InsetsController.class); mPendingInsetsController.replayAndAttach(secondController); diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java index 02a88fc8aecb..5ea071835de2 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.assertThrows; +import android.graphics.Insets; import android.view.View; import android.view.ViewStructure; import android.view.autofill.AutofillId; @@ -172,6 +173,11 @@ public class ContentCaptureSessionTest { } @Override + void internalNotifyViewInsetsChanged(Insets viewInsets) { + throw new UnsupportedOperationException("should not have been called"); + } + + @Override public void updateContentCaptureContext(ContentCaptureContext context) { throw new UnsupportedOperationException("should not have been called"); } diff --git a/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java b/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java new file mode 100644 index 000000000000..e4cfc53cc53a --- /dev/null +++ b/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.textclassifier; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SystemTextClassifierMetadataTest { + + @Test + public void testInvalidPackageNameThrowsException() { + assertThrows(NullPointerException.class, + () -> new SystemTextClassifierMetadata(/* packageName= */ null, /* userId= */ + 1, /* useDefaultTextClassifier= */ false)); + } + + @Test + public void testParcel() { + SystemTextClassifierMetadata sysTcMetadata = new SystemTextClassifierMetadata( + "package", /* userId= */ 1, /* useDefaultTextClassifier= */ false); + + Parcel p = Parcel.obtain(); + sysTcMetadata.writeToParcel(p, 0); + p.setDataPosition(0); + + SystemTextClassifierMetadata targetSysTcMetadata = + SystemTextClassifierMetadata.CREATOR.createFromParcel(p); + + assertThat(targetSysTcMetadata.getUserId()).isEqualTo(sysTcMetadata.getUserId()); + assertThat(targetSysTcMetadata.getCallingPackageName()).isEqualTo( + sysTcMetadata.getCallingPackageName()); + assertThat(targetSysTcMetadata.useDefaultTextClassifier()).isEqualTo( + sysTcMetadata.useDefaultTextClassifier()); + } +} diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java index d54402975f65..39ededaf1f67 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java @@ -17,6 +17,7 @@ package android.view.textclassifier; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -199,7 +200,9 @@ public class TextClassificationTest { .setReferenceTime(referenceTime) .setExtras(BUNDLE) .build(); - reference.setCallingPackageName(packageName); + final SystemTextClassifierMetadata systemTcMetadata = + new SystemTextClassifierMetadata(packageName, 1, false); + reference.setSystemTextClassifierMetadata(systemTcMetadata); // Parcel and unparcel. final Parcel parcel = Parcel.obtain(); @@ -216,5 +219,11 @@ public class TextClassificationTest { assertEquals(referenceTime, result.getReferenceTime()); assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY)); assertEquals(packageName, result.getCallingPackageName()); + final SystemTextClassifierMetadata resultSystemTcMetadata = + result.getSystemTextClassifierMetadata(); + assertNotNull(resultSystemTcMetadata); + assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName()); + assertEquals(1, resultSystemTcMetadata.getUserId()); + assertFalse(resultSystemTcMetadata.useDefaultTextClassifier()); } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java index d0d32e3a507a..31f8029550dd 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java @@ -17,6 +17,8 @@ package android.view.textclassifier; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import android.icu.util.ULocale; import android.os.Bundle; @@ -75,7 +77,9 @@ public final class TextLanguageTest { final TextLanguage.Request reference = new TextLanguage.Request.Builder(text) .setExtras(bundle) .build(); - reference.setCallingPackageName(packageName); + final SystemTextClassifierMetadata systemTcMetadata = + new SystemTextClassifierMetadata(packageName, 1, false); + reference.setSystemTextClassifierMetadata(systemTcMetadata); final Parcel parcel = Parcel.obtain(); reference.writeToParcel(parcel, 0); @@ -85,5 +89,11 @@ public final class TextLanguageTest { assertEquals(text, result.getText()); assertEquals("bundle", result.getExtras().getString(bundleKey)); assertEquals(packageName, result.getCallingPackageName()); + final SystemTextClassifierMetadata resultSystemTcMetadata = + result.getSystemTextClassifierMetadata(); + assertNotNull(resultSystemTcMetadata); + assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName()); + assertEquals(1, resultSystemTcMetadata.getUserId()); + assertFalse(resultSystemTcMetadata.useDefaultTextClassifier()); } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java index ec5813fd4123..4f0b44bfb864 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java @@ -17,6 +17,8 @@ package android.view.textclassifier; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import android.os.Bundle; import android.os.LocaleList; @@ -115,7 +117,9 @@ public class TextLinksTest { .setExtras(BUNDLE) .setReferenceTime(referenceTime) .build(); - reference.setCallingPackageName(packageName); + final SystemTextClassifierMetadata systemTcMetadata = + new SystemTextClassifierMetadata(packageName, 1, false); + reference.setSystemTextClassifierMetadata(systemTcMetadata); // Parcel and unparcel. final Parcel parcel = Parcel.obtain(); @@ -132,5 +136,11 @@ public class TextLinksTest { assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY)); assertEquals(packageName, result.getCallingPackageName()); assertEquals(referenceTime, result.getReferenceTime()); + final SystemTextClassifierMetadata resultSystemTcMetadata = + result.getSystemTextClassifierMetadata(); + assertNotNull(resultSystemTcMetadata); + assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName()); + assertEquals(1, resultSystemTcMetadata.getUserId()); + assertFalse(resultSystemTcMetadata.useDefaultTextClassifier()); } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java index 30cc4e8990e0..82788c806fed 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java @@ -17,6 +17,8 @@ package android.view.textclassifier; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import android.os.Bundle; import android.os.LocaleList; @@ -82,7 +84,9 @@ public class TextSelectionTest { .setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY)) .setExtras(BUNDLE) .build(); - reference.setCallingPackageName(packageName); + final SystemTextClassifierMetadata systemTcMetadata = + new SystemTextClassifierMetadata(packageName, 1, false); + reference.setSystemTextClassifierMetadata(systemTcMetadata); // Parcel and unparcel. final Parcel parcel = Parcel.obtain(); @@ -96,5 +100,11 @@ public class TextSelectionTest { assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags()); assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY)); assertEquals(packageName, result.getCallingPackageName()); + final SystemTextClassifierMetadata resultSystemTcMetadata = + result.getSystemTextClassifierMetadata(); + assertNotNull(resultSystemTcMetadata); + assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName()); + assertEquals(1, resultSystemTcMetadata.getUserId()); + assertFalse(resultSystemTcMetadata.useDefaultTextClassifier()); } } diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml index 61281eea7134..efe658a80233 100644 --- a/data/etc/car/com.google.android.car.kitchensink.xml +++ b/data/etc/car/com.google.android.car.kitchensink.xml @@ -33,6 +33,7 @@ <permission name="android.permission.MODIFY_AUDIO_ROUTING"/> <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.MONITOR_INPUT"/> <permission name="android.permission.PROVIDE_TRUST_AGENT"/> <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/> <permission name="android.permission.REAL_GET_TASKS"/> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 49edcf7aa347..487a6e912238 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -164,6 +164,7 @@ applications that come with the platform <permission name="android.permission.REBOOT"/> <permission name="android.permission.REGISTER_CALL_PROVIDER"/> <permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/> + <permission name="android.permission.REGISTER_STATS_PULL_ATOM"/> <permission name="android.permission.SEND_RESPOND_VIA_MESSAGE"/> <permission name="android.permission.SET_TIME_ZONE"/> <permission name="android.permission.SHUTDOWN"/> diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index d9d2eea3536e..a7d0cb84f848 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -764,8 +764,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private @KeyProperties.BlockModeEnum String[] mBlockModes; private boolean mRandomizedEncryptionRequired = true; private boolean mUserAuthenticationRequired; - private int mUserAuthenticationValidityDurationSeconds = -1; - private @KeyProperties.AuthEnum int mUserAuthenticationType; + private int mUserAuthenticationValidityDurationSeconds = 0; + private @KeyProperties.AuthEnum int mUserAuthenticationType = + KeyProperties.AUTH_BIOMETRIC_STRONG; private boolean mUserPresenceRequired = false; private byte[] mAttestationChallenge = null; private boolean mUniqueIdIncluded = false; @@ -1240,7 +1241,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu if (seconds == -1) { return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG); } - return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG); + return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_DEVICE_CREDENTIAL + | KeyProperties.AUTH_BIOMETRIC_STRONG); } /** diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index 8120a93e30e9..2e793dea3e05 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -562,8 +562,9 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private @KeyProperties.BlockModeEnum String[] mBlockModes; private boolean mRandomizedEncryptionRequired = true; private boolean mUserAuthenticationRequired; - private @KeyProperties.AuthEnum int mUserAuthenticationType; - private int mUserAuthenticationValidityDurationSeconds = -1; + private int mUserAuthenticationValidityDurationSeconds = 0; + private @KeyProperties.AuthEnum int mUserAuthenticationType = + KeyProperties.AUTH_BIOMETRIC_STRONG; private boolean mUserPresenceRequired = false; private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; @@ -870,7 +871,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { if (seconds == -1) { return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG); } - return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG); + return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_DEVICE_CREDENTIAL + | KeyProperties.AUTH_BIOMETRIC_STRONG); } /** diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java index 4ead253f3eea..bc933ff13825 100644 --- a/keystore/java/android/security/keystore/KeymasterUtils.java +++ b/keystore/java/android/security/keystore/KeymasterUtils.java @@ -165,8 +165,7 @@ public abstract class KeymasterUtils { } args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid)); - args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, - KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_BIOMETRIC); + args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType()); args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, spec.getUserAuthenticationValidityDurationSeconds()); if (spec.isUserAuthenticationValidWhileOnBody()) { diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 550e41f28f85..ff7049ee565b 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -612,7 +612,7 @@ public class LocationManager { public Location getLastLocation() { try { return mService.getLastLocation(null, mContext.getPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -645,7 +645,7 @@ public class LocationManager { try { return mService.getLastLocation(request, mContext.getPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -742,7 +742,7 @@ public class LocationManager { try { if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal, - listenerTransport, mContext.getPackageName(), mContext.getFeatureId(), + listenerTransport, mContext.getPackageName(), mContext.getAttributionTag(), getListenerIdentifier(consumer))) { listenerTransport.register(mContext.getSystemService(AlarmManager.class), remoteCancellationSignal); @@ -1189,7 +1189,7 @@ public class LocationManager { boolean registered = false; try { mService.requestLocationUpdates(locationRequest, transport, null, - mContext.getPackageName(), mContext.getFeatureId(), + mContext.getPackageName(), mContext.getAttributionTag(), getListenerIdentifier(listener)); registered = true; } catch (RemoteException e) { @@ -1235,7 +1235,7 @@ public class LocationManager { try { mService.requestLocationUpdates(locationRequest, null, pendingIntent, - mContext.getPackageName(), mContext.getFeatureId(), + mContext.getPackageName(), mContext.getAttributionTag(), getListenerIdentifier(pendingIntent)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1711,7 +1711,7 @@ public class LocationManager { LocationRequest request = new LocationRequest().setExpireIn(expiration); try { mService.requestGeofence(request, fence, intent, mContext.getPackageName(), - mContext.getFeatureId(), getListenerIdentifier(intent)); + mContext.getAttributionTag(), getListenerIdentifier(intent)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1798,7 +1798,7 @@ public class LocationManager { try { mService.requestGeofence(request, fence, intent, mContext.getPackageName(), - mContext.getFeatureId(), getListenerIdentifier(intent)); + mContext.getAttributionTag(), getListenerIdentifier(intent)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2946,7 +2946,7 @@ public class LocationManager { GnssStatusListener transport = new GnssStatusListener(); if (mService.registerGnssStatusCallback(transport, mContext.getPackageName(), - mContext.getFeatureId())) { + mContext.getAttributionTag())) { mListenerTransport = transport; return true; } else { @@ -3012,7 +3012,7 @@ public class LocationManager { GnssMeasurementsListener transport = new GnssMeasurementsListener(); if (mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(), - mContext.getFeatureId(), "gnss measurement callback")) { + mContext.getAttributionTag(), "gnss measurement callback")) { mListenerTransport = transport; return true; } else { @@ -3065,7 +3065,7 @@ public class LocationManager { GnssNavigationMessageListener transport = new GnssNavigationMessageListener(); if (mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(), - mContext.getFeatureId(), "gnss navigation callback")) { + mContext.getAttributionTag(), "gnss navigation callback")) { mListenerTransport = transport; return true; } else { @@ -3106,7 +3106,7 @@ public class LocationManager { GnssAntennaInfoListener transport = new GnssAntennaInfoListener(); if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(), - mContext.getFeatureId(), "gnss antenna info callback")) { + mContext.getAttributionTag(), "gnss antenna info callback")) { mListenerTransport = transport; return true; } else { @@ -3143,7 +3143,7 @@ public class LocationManager { BatchedLocationCallback transport = new BatchedLocationCallback(); if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(), - mContext.getFeatureId(), "batched location callback")) { + mContext.getAttributionTag(), "batched location callback")) { mListenerTransport = transport; return true; } else { diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java index 7932dcb06d88..ea066321f376 100644 --- a/media/java/android/media/tv/tuner/Lnb.java +++ b/media/java/android/media/tv/tuner/Lnb.java @@ -156,7 +156,7 @@ public class Lnb implements AutoCloseable { private long mNativeContext; - Lnb(int id) { + private Lnb(int id) { mId = id; } diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index bcbc12b51a73..08a33f1f861c 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -43,7 +43,11 @@ import android.media.tv.tuner.frontend.FrontendStatus; import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType; import android.media.tv.tuner.frontend.OnTuneEventListener; import android.media.tv.tuner.frontend.ScanCallback; +import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerLnbRequest; +import android.media.tv.tunerresourcemanager.TunerResourceManager; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.Message; @@ -67,6 +71,7 @@ public class Tuner implements AutoCloseable { private static final String TAG = "MediaTvTuner"; private static final boolean DEBUG = false; + private static final int MSG_RESOURCE_LOST = 1; private static final int MSG_ON_FILTER_EVENT = 2; private static final int MSG_ON_FILTER_STATUS = 3; private static final int MSG_ON_LNB_EVENT = 4; @@ -93,6 +98,8 @@ public class Tuner implements AutoCloseable { } private final Context mContext; + private final TunerResourceManager mTunerResourceManager; + private final int mClientId; private List<Integer> mFrontendIds; private Frontend mFrontend; @@ -102,6 +109,7 @@ public class Tuner implements AutoCloseable { private List<Integer> mLnbIds; private Lnb mLnb; + private Integer mLnbId; @Nullable private OnTuneEventListener mOnTuneEventListener; @Nullable @@ -115,6 +123,15 @@ public class Tuner implements AutoCloseable { @Nullable private Executor mOnResourceLostListenerExecutor; + + private final TunerResourceManager.ResourcesReclaimListener mResourceListener = + new TunerResourceManager.ResourcesReclaimListener() { + @Override + public void onReclaimResources() { + mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST)); + } + }; + /** * Constructs a Tuner instance. * @@ -127,6 +144,14 @@ public class Tuner implements AutoCloseable { @TvInputService.PriorityHintUseCaseType int useCase) { nativeSetup(); mContext = context; + mTunerResourceManager = (TunerResourceManager) + context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE); + + int[] clientId = new int[1]; + ResourceClientProfile profile = new ResourceClientProfile(tvInputSessionId, useCase); + mTunerResourceManager.registerClientProfile( + profile, new HandlerExecutor(mHandler), mResourceListener, clientId); + mClientId = clientId[0]; } /** @@ -226,6 +251,7 @@ public class Tuner implements AutoCloseable { private native List<Integer> nativeGetLnbIds(); private native Lnb nativeOpenLnbById(int id); + private native Lnb nativeOpenLnbByName(String name); private native Descrambler nativeOpenDescrambler(); @@ -275,6 +301,14 @@ public class Tuner implements AutoCloseable { } break; } + case MSG_RESOURCE_LOST: { + if (mOnResourceLostListener != null + && mOnResourceLostListenerExecutor != null) { + mOnResourceLostListenerExecutor.execute( + () -> mOnResourceLostListener.onResourceLost(Tuner.this)); + } + break; + } default: // fall through } @@ -698,7 +732,10 @@ public class Tuner implements AutoCloseable { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(cb, "LnbCallback must not be null"); TunerUtils.checkTunerPermission(mContext); - return openLnbByName(null, executor, cb); + if (mLnbId == null && !requestLnb()) { + return null; + } + return nativeOpenLnbById(mLnbId); } /** @@ -714,11 +751,21 @@ public class Tuner implements AutoCloseable { @Nullable public Lnb openLnbByName(@NonNull String name, @CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) { + Objects.requireNonNull(name, "LNB name must not be null"); Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(cb, "LnbCallback must not be null"); TunerUtils.checkTunerPermission(mContext); - // TODO: use resource manager to get LNB ID. - return new Lnb(0); + return nativeOpenLnbByName(name); + } + + private boolean requestLnb() { + int[] lnbId = new int[1]; + TunerLnbRequest request = new TunerLnbRequest(mClientId); + boolean granted = mTunerResourceManager.requestLnb(request, lnbId); + if (granted) { + mLnbId = lnbId[0]; + } + return granted; } /** diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java index dbd9db4b6a45..37a016ecb4ce 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java +++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java @@ -69,7 +69,7 @@ public class DvrPlayback implements AutoCloseable { */ public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL; - long mNativeContext; + private long mNativeContext; private native int nativeAttachFilter(Filter filter); private native int nativeDetachFilter(Filter filter); diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java index c1c6c624454e..d06356cfb6de 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java +++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java @@ -30,7 +30,7 @@ import android.os.ParcelFileDescriptor; */ @SystemApi public class DvrRecorder implements AutoCloseable { - long mNativeContext; + private long mNativeContext; private native int nativeAttachFilter(Filter filter); private native int nativeDetachFilter(Filter filter); diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java index e99fd36f98a7..1510b2d399f7 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java @@ -125,9 +125,9 @@ public class DvbtFrontendSettings extends FrontendSettings { /** @hide */ @IntDef(flag = true, prefix = "CONSTELLATION_", - value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_CONSTELLATION_QPSK, - CONSTELLATION_CONSTELLATION_16QAM, CONSTELLATION_CONSTELLATION_64QAM, - CONSTELLATION_CONSTELLATION_256QAM}) + value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_QPSK, + CONSTELLATION_16QAM, CONSTELLATION_64QAM, + CONSTELLATION_256QAM}) @Retention(RetentionPolicy.SOURCE) public @interface Constellation {} @@ -142,22 +142,22 @@ public class DvbtFrontendSettings extends FrontendSettings { /** * QPSK Constellation. */ - public static final int CONSTELLATION_CONSTELLATION_QPSK = + public static final int CONSTELLATION_QPSK = Constants.FrontendDvbtConstellation.CONSTELLATION_QPSK; /** * 16QAM Constellation. */ - public static final int CONSTELLATION_CONSTELLATION_16QAM = + public static final int CONSTELLATION_16QAM = Constants.FrontendDvbtConstellation.CONSTELLATION_16QAM; /** * 64QAM Constellation. */ - public static final int CONSTELLATION_CONSTELLATION_64QAM = + public static final int CONSTELLATION_64QAM = Constants.FrontendDvbtConstellation.CONSTELLATION_64QAM; /** * 256QAM Constellation. */ - public static final int CONSTELLATION_CONSTELLATION_256QAM = + public static final int CONSTELLATION_256QAM = Constants.FrontendDvbtConstellation.CONSTELLATION_256QAM; @@ -275,11 +275,11 @@ public class DvbtFrontendSettings extends FrontendSettings { @IntDef(flag = true, prefix = "GUARD_INTERVAL_", value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO, - GUARD_INTERVAL_INTERVAL_1_32, GUARD_INTERVAL_INTERVAL_1_16, - GUARD_INTERVAL_INTERVAL_1_8, GUARD_INTERVAL_INTERVAL_1_4, - GUARD_INTERVAL_INTERVAL_1_128, - GUARD_INTERVAL_INTERVAL_19_128, - GUARD_INTERVAL_INTERVAL_19_256}) + GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, + GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4, + GUARD_INTERVAL_1_128, + GUARD_INTERVAL_19_128, + GUARD_INTERVAL_19_256}) @Retention(RetentionPolicy.SOURCE) public @interface GuardInterval {} @@ -295,37 +295,37 @@ public class DvbtFrontendSettings extends FrontendSettings { /** * 1/32 Guard Interval. */ - public static final int GUARD_INTERVAL_INTERVAL_1_32 = + public static final int GUARD_INTERVAL_1_32 = Constants.FrontendDvbtGuardInterval.INTERVAL_1_32; /** * 1/16 Guard Interval. */ - public static final int GUARD_INTERVAL_INTERVAL_1_16 = + public static final int GUARD_INTERVAL_1_16 = Constants.FrontendDvbtGuardInterval.INTERVAL_1_16; /** * 1/8 Guard Interval. */ - public static final int GUARD_INTERVAL_INTERVAL_1_8 = + public static final int GUARD_INTERVAL_1_8 = Constants.FrontendDvbtGuardInterval.INTERVAL_1_8; /** * 1/4 Guard Interval. */ - public static final int GUARD_INTERVAL_INTERVAL_1_4 = + public static final int GUARD_INTERVAL_1_4 = Constants.FrontendDvbtGuardInterval.INTERVAL_1_4; /** * 1/128 Guard Interval. */ - public static final int GUARD_INTERVAL_INTERVAL_1_128 = + public static final int GUARD_INTERVAL_1_128 = Constants.FrontendDvbtGuardInterval.INTERVAL_1_128; /** * 19/128 Guard Interval. */ - public static final int GUARD_INTERVAL_INTERVAL_19_128 = + public static final int GUARD_INTERVAL_19_128 = Constants.FrontendDvbtGuardInterval.INTERVAL_19_128; /** * 19/256 Guard Interval. */ - public static final int GUARD_INTERVAL_INTERVAL_19_256 = + public static final int GUARD_INTERVAL_19_256 = Constants.FrontendDvbtGuardInterval.INTERVAL_19_256; /** @hide */ @@ -435,14 +435,14 @@ public class DvbtFrontendSettings extends FrontendSettings { * Gets Code Rate for High Priority level. */ @CodeRate - public int getHpCodeRate() { + public int getHighPriorityCodeRate() { return mHpCodeRate; } /** * Gets Code Rate for Low Priority level. */ @CodeRate - public int getLpCodeRate() { + public int getLowPriorityCodeRate() { return mLpCodeRate; } /** @@ -560,7 +560,7 @@ public class DvbtFrontendSettings extends FrontendSettings { * Sets Code Rate for High Priority level. */ @NonNull - public Builder setHpCodeRate(@CodeRate int hpCodeRate) { + public Builder setHighPriorityCodeRate(@CodeRate int hpCodeRate) { mHpCodeRate = hpCodeRate; return this; } @@ -568,7 +568,7 @@ public class DvbtFrontendSettings extends FrontendSettings { * Sets Code Rate for Low Priority level. */ @NonNull - public Builder setLpCodeRate(@CodeRate int lpCodeRate) { + public Builder setLowPriorityCodeRate(@CodeRate int lpCodeRate) { mLpCodeRate = lpCodeRate; return this; } diff --git a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index 20efaa1e0833..77cac6ef7e2e 100644 --- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -19,6 +19,8 @@ package android.media.tv.tunerresourcemanager; import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerDemuxRequest; +import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; @@ -148,6 +150,53 @@ interface ITunerResourceManager { void shareFrontend(in int selfClientId, in int targetClientId); /* + * This API is used by the Tuner framework to request an available demux from the TunerHAL. + * + * <p>There are three possible scenarios: + * <ul> + * <li>If there is demux available, the API would send the handle back. + * + * <li>If no Demux is available but the current request info can show higher priority than + * other uses of demuxes, the API will send + * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + * <li>If no demux can be granted, the API would return false. + * <ul> + * + * @param request {@link TunerDemuxRequest} information of the current request. + * @param demuxHandle a one-element array to return the granted demux handle. + * + * @return true if there is demux granted. + */ + boolean requestDemux(in TunerDemuxRequest request, out int[] demuxHandle); + + /* + * This API is used by the Tuner framework to request an available descrambler from the + * TunerHAL. + * + * <p>There are three possible scenarios: + * <ul> + * <li>If there is descrambler available, the API would send the handle back. + * + * <li>If no Descrambler is available but the current request info can show higher priority than + * other uses of Descrambler, the API will send + * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + * <li>If no Descrambler can be granted, the API would return false. + * <ul> + * + * @param request {@link TunerDescramblerRequest} information of the current request. + * @param descramblerHandle a one-element array to return the granted descrambler handle. + * + * @return true if there is Descrambler granted. + */ + boolean requestDescrambler(in TunerDescramblerRequest request, out int[] descramblerHandle); + + /* * This API is used by the Tuner framework to request an available Cas session. This session * needs to be under the CAS system with the id indicated in the {@code request}. * @@ -210,6 +259,24 @@ interface ITunerResourceManager { void releaseFrontend(in int frontendId); /* + * Notifies the TRM that the Demux with the given handle was released. + * + * <p>Client must call this whenever it releases a demux. + * + * @param demuxHandle the handle of the released Tuner Demux. + */ + void releaseDemux(in int demuxHandle); + + /* + * Notifies the TRM that the Descrambler with the given handle was released. + * + * <p>Client must call this whenever it releases a descrambler. + * + * @param demuxHandle the handle of the released Tuner Descrambler. + */ + void releaseDescrambler(in int descramblerHandle); + + /* * Notifies the TRM that the given Cas session has been released. * * <p>Client must call this whenever it releases a Cas session. diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl new file mode 100644 index 000000000000..919a215a9ce5 --- /dev/null +++ b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl @@ -0,0 +1,24 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tunerresourcemanager; + +/** + * Information required to request a Tuner Demux. + * + * @hide + */ +parcelable TunerDemuxRequest;
\ No newline at end of file diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java new file mode 100644 index 000000000000..34a77616f62e --- /dev/null +++ b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tunerresourcemanager; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * Information required to request a Tuner Demux. + * + * @hide + */ +public final class TunerDemuxRequest implements Parcelable { + static final String TAG = "TunerDemuxRequest"; + + public static final + @NonNull + Parcelable.Creator<TunerDemuxRequest> CREATOR = + new Parcelable.Creator<TunerDemuxRequest>() { + @Override + public TunerDemuxRequest createFromParcel(Parcel source) { + try { + return new TunerDemuxRequest(source); + } catch (Exception e) { + Log.e(TAG, "Exception creating TunerDemuxRequest from parcel", e); + return null; + } + } + + @Override + public TunerDemuxRequest[] newArray(int size) { + return new TunerDemuxRequest[size]; + } + }; + + /** + * Client id of the client that sends the request. + */ + private final int mClientId; + + private TunerDemuxRequest(@NonNull Parcel source) { + mClientId = source.readInt(); + } + + /** + * Constructs a new {@link TunerDemuxRequest} with the given parameters. + * + * @param clientId id of the client. + */ + public TunerDemuxRequest(int clientId) { + mClientId = clientId; + } + + /** + * Returns the id of the client. + */ + public int getClientId() { + return mClientId; + } + + // Parcelable + @Override + public int describeContents() { + return 0; + } + + @NonNull + @Override + public String toString() { + StringBuilder b = new StringBuilder(128); + b.append("TunerDemuxRequest {clientId=").append(mClientId); + b.append("}"); + return b.toString(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mClientId); + } +} diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl new file mode 100644 index 000000000000..fbafb3bc010e --- /dev/null +++ b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl @@ -0,0 +1,24 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tunerresourcemanager; + +/** + * Information required to request a Tuner Descrambler. + * + * @hide + */ +parcelable TunerDescramblerRequest;
\ No newline at end of file diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java new file mode 100644 index 000000000000..58162879f8cf --- /dev/null +++ b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tunerresourcemanager; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * Information required to request a Tuner Descrambler. + * + * @hide + */ +public final class TunerDescramblerRequest implements Parcelable { + static final String TAG = "TunerDescramblerRequest"; + + public static final + @NonNull + Parcelable.Creator<TunerDescramblerRequest> CREATOR = + new Parcelable.Creator<TunerDescramblerRequest>() { + @Override + public TunerDescramblerRequest createFromParcel(Parcel source) { + try { + return new TunerDescramblerRequest(source); + } catch (Exception e) { + Log.e(TAG, "Exception creating TunerDescramblerRequest from parcel", e); + return null; + } + } + + @Override + public TunerDescramblerRequest[] newArray(int size) { + return new TunerDescramblerRequest[size]; + } + }; + + /** + * Client id of the client that sends the request. + */ + private final int mClientId; + + private TunerDescramblerRequest(@NonNull Parcel source) { + mClientId = source.readInt(); + } + + /** + * Constructs a new {@link TunerDescramblerRequest} with the given parameters. + * + * @param clientId id of the client. + */ + public TunerDescramblerRequest(int clientId) { + mClientId = clientId; + } + + /** + * Returns the id of the client. + */ + public int getClientId() { + return mClientId; + } + + // Parcelable + @Override + public int describeContents() { + return 0; + } + + @NonNull + @Override + public String toString() { + StringBuilder b = new StringBuilder(128); + b.append("TunerDescramblerRequest {clientId=").append(mClientId); + b.append("}"); + return b.toString(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mClientId); + } +} diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index 7c11ed485ced..524b6c2e26e1 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -65,6 +65,7 @@ public class TunerResourceManager { public static final int INVALID_LNB_ID = -1; public static final int INVALID_TV_INPUT_DEVICE_ID = -1; public static final int INVALID_TV_INPUT_PORT_ID = -1; + public static final int INVALID_RESOURCE_HANDLE = -1; private final ITunerResourceManager mService; private final int mUserId; @@ -260,6 +261,71 @@ public class TunerResourceManager { } /** + * Requests a Tuner Demux resource. + * + * <p>There are three possible scenarios: + * <ul> + * <li>If there is Demux available, the API would send the handle back. + * + * <li>If no Demux is available but the current request has a higher priority than other uses of + * demuxes, the API will send {@link IResourcesReclaimListener#onReclaimResources()} to the + * {@link Tuner}. Tuner would handle the resource reclaim on the holder of lower priority and + * notify the holder of its resource loss. + * + * <li>If no Demux system can be granted, the API would return false. + * <ul> + * + * @param request {@link TunerDemuxRequest} information of the current request. + * @param demuxHandle a one-element array to return the granted Demux handle. + * If no Demux granted, this will return {@link #INVALID_RESOURCE_HANDLE}. + * + * @return true if there is Demux granted. + */ + public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull int[] demuxHandle) { + boolean result = false; + try { + result = mService.requestDemux(request, demuxHandle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return result; + } + + /** + * Requests a Tuner Descrambler resource. + * + * <p>There are three possible scenarios: + * <ul> + * <li>If there is Descrambler available, the API would send the handle back. + * + * <li>If no Descrambler is available but the current request has a higher priority than other + * uses of descramblers, the API will send + * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + * <li>If no Descrambler system can be granted, the API would return false. + * <ul> + * + * @param request {@link TunerDescramblerRequest} information of the current request. + * @param descramblerHandle a one-element array to return the granted Descrambler handle. + * If no Descrambler granted, this will return + * {@link #INVALID_RESOURCE_HANDLE}. + * + * @return true if there is Descrambler granted. + */ + public boolean requestDescrambler(@NonNull TunerDescramblerRequest request, + @NonNull int[] descramblerHandle) { + boolean result = false; + try { + result = mService.requestDescrambler(request, descramblerHandle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return result; + } + + /** * Requests a CAS session resource. * * <p>There are three possible scenarios: @@ -345,6 +411,36 @@ public class TunerResourceManager { } /** + * Notifies the TRM that the Demux with the given handle has been released. + * + * <p>Client must call this whenever it releases an Demux. + * + * @param demuxHandle the handle of the released Tuner Demux. + */ + public void releaseDemux(int demuxHandle) { + try { + mService.releaseDemux(demuxHandle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Notifies the TRM that the Descrambler with the given handle has been released. + * + * <p>Client must call this whenever it releases an Descrambler. + * + * @param descramblerHandle the handle of the released Tuner Descrambler. + */ + public void releaseDescrambler(int descramblerHandle) { + try { + mService.releaseDescrambler(descramblerHandle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Notifies the TRM that the given Cas session has been released. * * <p>Client must call this whenever it releases a Cas session. diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 01f068a05fc3..a37c9e52f5dd 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -138,11 +138,13 @@ struct fields_t { jfieldID filterContext; jfieldID timeFilterContext; jfieldID descramblerContext; - jfieldID dvrContext; + jfieldID dvrRecorderContext; + jfieldID dvrPlaybackContext; jmethodID frontendInitID; jmethodID filterInitID; jmethodID timeFilterInitID; - jmethodID dvrInitID; + jmethodID dvrRecorderInitID; + jmethodID dvrPlaybackInitID; jmethodID onFrontendEventID; jmethodID onFilterStatusID; jmethodID onFilterEventID; @@ -1072,6 +1074,37 @@ jobject JTuner::openLnbById(int id) { return lnbObj; } +jobject JTuner::openLnbByName(jstring name) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + std::string lnbName(env->GetStringUTFChars(name, nullptr)); + sp<ILnb> iLnbSp; + Result res; + LnbId id; + mTuner->openLnbByName(lnbName, [&](Result r, LnbId lnbId, const sp<ILnb>& lnb) { + res = r; + iLnbSp = lnb; + id = lnbId; + }); + if (res != Result::SUCCESS || iLnbSp == nullptr) { + ALOGE("Failed to open lnb"); + return NULL; + } + mLnb = iLnbSp; + sp<LnbCallback> lnbCb = new LnbCallback(mObject, id); + mLnb->setCallback(lnbCb); + + jobject lnbObj = env->NewObject( + env->FindClass("android/media/tv/tuner/Lnb"), + gFields.lnbInitID, + id); + + sp<Lnb> lnbSp = new Lnb(iLnbSp, lnbObj); + lnbSp->incStrong(lnbObj); + env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get()); + + return lnbObj; +} + int JTuner::tune(const FrontendSettings& settings) { if (mFe == NULL) { ALOGE("frontend is not initialized"); @@ -1302,7 +1335,7 @@ jobject JTuner::openTimeFilter() { return timeFilterObj; } -jobject JTuner::openDvr(DvrType type, int bufferSize) { +jobject JTuner::openDvr(DvrType type, jlong bufferSize) { ALOGD("JTuner::openDvr"); if (mDemux == NULL) { if (openDemux() != Result::SUCCESS) { @@ -1311,24 +1344,38 @@ jobject JTuner::openDvr(DvrType type, int bufferSize) { } sp<IDvr> iDvrSp; sp<DvrCallback> callback = new DvrCallback(); - mDemux->openDvr(type, bufferSize, callback, - [&](Result, const sp<IDvr>& dvr) { + Result res; + mDemux->openDvr(type, (uint32_t) bufferSize, callback, + [&](Result r, const sp<IDvr>& dvr) { + res = r; iDvrSp = dvr; }); - if (iDvrSp == NULL) { + if (res != Result::SUCCESS || iDvrSp == NULL) { return NULL; } JNIEnv *env = AndroidRuntime::getJNIEnv(); - jobject dvrObj = - env->NewObject( - env->FindClass("android/media/tv/tuner/dvr/Dvr"), - gFields.dvrInitID, - mObject); - sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj); - dvrSp->incStrong(dvrObj); - env->SetLongField(dvrObj, gFields.dvrContext, (jlong)dvrSp.get()); + jobject dvrObj; + if (type == DvrType::RECORD) { + dvrObj = + env->NewObject( + env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"), + gFields.dvrRecorderInitID, + mObject); + sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj); + dvrSp->incStrong(dvrObj); + env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrSp.get()); + } else { + dvrObj = + env->NewObject( + env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"), + gFields.dvrPlaybackInitID, + mObject); + sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj); + dvrSp->incStrong(dvrObj); + env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrSp.get()); + } callback->setDvr(dvrObj); @@ -1852,7 +1899,11 @@ static DvrSettings getDvrSettings(JNIEnv *env, jobject settings) { } static sp<Dvr> getDvr(JNIEnv *env, jobject dvr) { - return (Dvr *)env->GetLongField(dvr, gFields.dvrContext); + bool isRecorder = + env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder")); + jfieldID fieldId = + isRecorder ? gFields.dvrRecorderContext : gFields.dvrPlaybackContext; + return (Dvr *)env->GetLongField(dvr, fieldId); } static void android_media_tv_Tuner_native_init(JNIEnv *env) { @@ -1872,8 +1923,7 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { jclass lnbClazz = env->FindClass("android/media/tv/tuner/Lnb"); gFields.lnbContext = env->GetFieldID(lnbClazz, "mNativeContext", "J"); - gFields.lnbInitID = - env->GetMethodID(lnbClazz, "<init>", "(I)V"); + gFields.lnbInitID = env->GetMethodID(lnbClazz, "<init>", "(I)V"); jclass filterClazz = env->FindClass("android/media/tv/tuner/filter/Filter"); gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J"); @@ -1894,9 +1944,13 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { gFields.descramblerInitID = env->GetMethodID(descramblerClazz, "<init>", "()V"); - jclass dvrClazz = env->FindClass("android/media/tv/tuner/dvr/Dvr"); - gFields.dvrContext = env->GetFieldID(dvrClazz, "mNativeContext", "J"); - gFields.dvrInitID = env->GetMethodID(dvrClazz, "<init>", "()V"); + jclass dvrRecorderClazz = env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"); + gFields.dvrRecorderContext = env->GetFieldID(dvrRecorderClazz, "mNativeContext", "J"); + gFields.dvrRecorderInitID = env->GetMethodID(dvrRecorderClazz, "<init>", "()V"); + + jclass dvrPlaybackClazz = env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"); + gFields.dvrPlaybackContext = env->GetFieldID(dvrPlaybackClazz, "mNativeContext", "J"); + gFields.dvrPlaybackInitID = env->GetMethodID(dvrPlaybackClazz, "<init>", "()V"); jclass linearBlockClazz = env->FindClass("android/media/MediaCodec$LinearBlock"); gFields.linearBlockInitID = env->GetMethodID(linearBlockClazz, "<init>", "()V"); @@ -1996,6 +2050,12 @@ static jobject android_media_tv_Tuner_open_lnb_by_id(JNIEnv *env, jobject thiz, return tuner->openLnbById(id); } +static jobject android_media_tv_Tuner_open_lnb_by_name(JNIEnv *env, jobject thiz, jstring name) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->openLnbByName(name); +} + + static jobject android_media_tv_Tuner_open_filter( JNIEnv *env, jobject thiz, jint type, jint subType, jlong bufferSize) { sp<JTuner> tuner = getTuner(env, thiz); @@ -2618,13 +2678,15 @@ static int android_media_tv_Tuner_close_descrambler(JNIEnv, jobject) { } static jobject android_media_tv_Tuner_open_dvr_recorder( - JNIEnv* /* env */, jobject /* thiz */, jlong /* bufferSize */) { - return NULL; + JNIEnv* env, jobject thiz, jlong bufferSize) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->openDvr(DvrType::RECORD, bufferSize); } static jobject android_media_tv_Tuner_open_dvr_playback( - JNIEnv* /* env */, jobject /* thiz */, jlong /* bufferSize */) { - return NULL; + JNIEnv* env, jobject thiz, jlong bufferSize) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->openDvr(DvrType::PLAYBACK, bufferSize); } static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) { @@ -2678,11 +2740,13 @@ static int android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobjec } static int android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) { + sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr(); if (dvrSp == NULL) { ALOGD("Failed to start dvr: dvr not found"); return false; } + Result result = dvrSp->start(); return (int) result; } @@ -2888,6 +2952,8 @@ static const JNINativeMethod gTunerMethods[] = { (void *)android_media_tv_Tuner_get_lnb_ids }, { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Lnb;", (void *)android_media_tv_Tuner_open_lnb_by_id }, + { "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;", + (void *)android_media_tv_Tuner_open_lnb_by_name }, { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Descrambler;", (void *)android_media_tv_Tuner_open_descrambler }, { "nativeOpenDvrRecorder", "(J)Landroid/media/tv/tuner/dvr/DvrRecorder;", @@ -2930,7 +2996,7 @@ static const JNINativeMethod gDescramblerMethods[] = { { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_descrambler }, }; -static const JNINativeMethod gDvrMethods[] = { +static const JNINativeMethod gDvrRecorderMethods[] = { { "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I", (void *)android_media_tv_Tuner_attach_filter }, { "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I", @@ -2942,14 +3008,22 @@ static const JNINativeMethod gDvrMethods[] = { { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr }, { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr }, { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd }, -}; - -static const JNINativeMethod gDvrRecorderMethods[] = { { "nativeWrite", "(J)J", (void *)android_media_tv_Tuner_write_dvr }, { "nativeWrite", "([BJJ)J", (void *)android_media_tv_Tuner_write_dvr_to_array }, }; static const JNINativeMethod gDvrPlaybackMethods[] = { + { "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I", + (void *)android_media_tv_Tuner_attach_filter }, + { "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I", + (void *)android_media_tv_Tuner_detach_filter }, + { "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I", + (void *)android_media_tv_Tuner_configure_dvr }, + { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr }, + { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr }, + { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr }, + { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr }, + { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd }, { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr }, { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array }, }; @@ -2990,13 +3064,6 @@ static bool register_android_media_tv_Tuner(JNIEnv *env) { return false; } if (AndroidRuntime::registerNativeMethods( - env, "android/media/tv/tuner/dvr/Dvr", - gDvrMethods, - NELEM(gDvrMethods)) != JNI_OK) { - ALOGE("Failed to register dvr native methods"); - return false; - } - if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/dvr/DvrRecorder", gDvrRecorderMethods, NELEM(gDvrRecorderMethods)) != JNI_OK) { diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index d3298a851fdd..5d2bba6c8aa2 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -180,10 +180,11 @@ struct JTuner : public RefBase { int setLna(bool enable); jobject getLnbIds(); jobject openLnbById(int id); + jobject openLnbByName(jstring name); jobject openFilter(DemuxFilterType type, int bufferSize); jobject openTimeFilter(); jobject openDescrambler(); - jobject openDvr(DvrType type, int bufferSize); + jobject openDvr(DvrType type, jlong bufferSize); protected: Result openDemux(); diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h index 15b39f2df891..30ad220db05b 100644 --- a/media/jni/soundpool/StreamManager.h +++ b/media/jni/soundpool/StreamManager.h @@ -70,9 +70,10 @@ private: static int staticFunction(void *data) { JavaThread *jt = static_cast<JavaThread *>(data); jt->mF(); + jt->mIsClosed = true; // set the flag that we are closed + // now before we allow the destructor to execute; + // otherwise there may be a use after free. jt->mPromise.set_value(); - jt->mIsClosed = true; // publicly inform that we are closed - // after we have accessed all variables. return 0; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index 6354ccd7b3b0..ebd7658381ee 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java @@ -284,12 +284,12 @@ public class CameraBinderTest extends AndroidTestCase { ICameraDeviceCallbacks dummyCallbacks = new DummyCameraDeviceCallbacks(); String clientPackageName = getContext().getPackageName(); - String clientFeatureId = getContext().getFeatureId(); + String clientAttributionTag = getContext().getAttributionTag(); ICameraDeviceUser cameraUser = mUtils.getCameraService().connectDevice( dummyCallbacks, String.valueOf(cameraId), - clientPackageName, clientFeatureId, + clientPackageName, clientAttributionTag, ICameraService.USE_CALLING_UID); assertNotNull(String.format("Camera %s was null", cameraId), cameraUser); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java index 466c5f4ba980..bf3e74602dbe 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java @@ -238,12 +238,12 @@ public class CameraDeviceBinderTest extends AndroidTestCase { ICameraDeviceCallbacks.Stub dummyCallbacks = new DummyCameraDeviceCallbacks(); String clientPackageName = getContext().getPackageName(); - String clientFeatureId = getContext().getFeatureId(); + String clientAttributionTag = getContext().getAttributionTag(); mMockCb = spy(dummyCallbacks); mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId, - clientPackageName, clientFeatureId, ICameraService.USE_CALLING_UID); + clientPackageName, clientAttributionTag, ICameraService.USE_CALLING_UID); assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser); mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index 140c075552c7..8292d304af2d 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -24,7 +24,6 @@ import android.content.Context; import com.android.keyguard.KeyguardViewController; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarDeviceProvisionedControllerImpl; -import com.android.systemui.car.CarNotificationInterruptionStateProvider; import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; @@ -41,7 +40,6 @@ import com.android.systemui.statusbar.car.CarShadeControllerImpl; import com.android.systemui.statusbar.car.CarStatusBar; import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; @@ -67,10 +65,6 @@ import dagger.Provides; @Module(includes = {DividerModule.class}) abstract class CarSystemUIModule { - @Binds - abstract NotificationInterruptionStateProvider bindNotificationInterruptionStateProvider( - CarNotificationInterruptionStateProvider notificationInterruptionStateProvider); - @Singleton @Provides @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java deleted file mode 100644 index 447e579ece42..000000000000 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.car; - -import android.content.Context; - -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.policy.BatteryController; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */ -@Singleton -public class CarNotificationInterruptionStateProvider extends - NotificationInterruptionStateProvider { - - @Inject - public CarNotificationInterruptionStateProvider(Context context, - NotificationFilter filter, - StatusBarStateController stateController, - BatteryController batteryController) { - super(context, filter, stateController, batteryController); - } - - @Override - public boolean shouldHeadsUp(NotificationEntry entry) { - // Because space is usually constrained in the auto use-case, there should not be a - // pinned notification when the shade has been expanded. Ensure this by not pinning any - // notification if the shade is already opened. - if (!getPresenter().isPresenterFullyCollapsed()) { - return false; - } - - return super.shouldHeadsUp(entry); - } -} diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index b2e21045f2a9..411f14d1d1ed 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -95,13 +95,15 @@ import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotificationAlertingManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.init.NotificationsController; +import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; +import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.AutoHideController; @@ -249,7 +251,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, - NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationInterruptStateProvider notificationInterruptStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, NotificationAlertingManager notificationAlertingManager, @@ -335,7 +337,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt remoteInputQuickSettingsDisabler, notificationGutsManager, notificationLogger, - notificationInterruptionStateProvider, + notificationInterruptStateProvider, notificationViewHierarchyManager, keyguardViewMediator, notificationAlertingManager, @@ -488,6 +490,22 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt .isCurrentUserSetupInProgress(); } }); + + mNotificationInterruptStateProvider.addSuppressor(new NotificationInterruptSuppressor() { + @Override + public String getName() { + return TAG; + } + + @Override + public boolean suppressInterruptions(NotificationEntry entry) { + // Because space is usually constrained in the auto use-case, there should not be a + // pinned notification when the shade has been expanded. + // Ensure this by not allowing any interruptions (ie: pinning any notifications) if + // the shade is already opened. + return !getPresenter().isPresenterFullyCollapsed(); + } + }); } @Override diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java index 4754118e7a64..160268b8f461 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -62,13 +62,13 @@ import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule; -import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotificationAlertingManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; +import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; +import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationRowModule; @@ -146,7 +146,7 @@ public class CarStatusBarModule { RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, - NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationInterruptStateProvider notificationInterruptionStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, NotificationAlertingManager notificationAlertingManager, diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java index f93faeb0c7ae..ace50f30663d 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java @@ -545,7 +545,7 @@ public class TileUtils { bundle.putString(META_DATA_PREFERENCE_KEYHINT, key); } try { - return provider.call(context.getPackageName(), context.getFeatureId(), + return provider.call(context.getPackageName(), context.getAttributionTag(), uri.getAuthority(), method, uri.toString(), bundle); } catch (RemoteException e) { return null; diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index e8e1d0b01471..00f6bcb6c548 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1039,7 +1039,7 @@ <!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] --> <string name="accessibility_display_daltonizer_preference_title">Color correction</string> <!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] --> - <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps people with colorblindness see more accurate colors</string> + <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps the device display more accurate colors. Color correction may be helpful for people with colorblindness.</string> <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] --> <string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string> diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java index 68a3729432d8..245b7843110b 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java @@ -8,7 +8,6 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.app.AppOpsManager.OpEntry; -import android.app.AppOpsManager.OpFeatureEntry; import android.app.AppOpsManager.PackageOps; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -163,6 +162,6 @@ public class RecentLocationAccessesTest { AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null)); return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null, - new OpFeatureEntry(op, false, accessEvents, null))); + new AppOpsManager.AttributedOpEntry(op, false, accessEvents, null))); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java index 3f8d758ae64f..cc9b93141d45 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.app.AppOpsManager.OpEntry; -import android.app.AppOpsManager.OpFeatureEntry; +import android.app.AppOpsManager.AttributedOpEntry; import android.app.AppOpsManager.PackageOps; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -18,8 +18,6 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.util.LongSparseArray; -import android.util.LongSparseLongArray; -import android.util.Pair; import org.junit.Before; import org.junit.Test; @@ -164,6 +162,6 @@ public class RecentLocationAppsTest { AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, duration, null)); return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null, - new OpFeatureEntry(op, false, accessEvents, null))); + new AttributedOpEntry(op, false, accessEvents, null))); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 1e888da6468e..18d8f14eb88d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -19,7 +19,6 @@ package com.android.settingslib.media; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -121,8 +120,6 @@ public class LocalMediaManagerTest { verify(currentDevice).disconnect(); verify(device).connect(); - verify(mCallback).onSelectedDeviceStateChanged(any(), - eq(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED)); } @Test @@ -368,7 +365,8 @@ public class LocalMediaManagerTest { mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2); assertThat(mLocalMediaManager.getCurrentConnectedDevice()).isEqualTo(device2); - verify(mCallback).onDeviceAttributesChanged(); + verify(mCallback).onSelectedDeviceStateChanged(device2, + LocalMediaManager.MediaDeviceState.STATE_CONNECTED); } @Test diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 8fa98c85fc33..d350d9df37b8 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -141,6 +141,8 @@ public class SecureSettings { Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK, Settings.Secure.UI_NIGHT_MODE, + Settings.Secure.DARK_THEME_CUSTOM_START_TIME, + Settings.Secure.DARK_THEME_CUSTOM_END_TIME, Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST, Settings.Secure.SKIP_DIRECTION, Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 75c5f9581820..4d33b627f4e2 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -28,6 +28,7 @@ import static android.provider.settings.validators.SettingsValidators.LOCALE_VAL import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR; @@ -235,7 +236,9 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(Secure.ODI_CAPTIONS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR); - VALIDATORS.put(Secure.UI_NIGHT_MODE, new InclusiveIntegerRangeValidator(0, 2)); + VALIDATORS.put(Secure.UI_NIGHT_MODE, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.DARK_THEME_CUSTOM_START_TIME, NONE_NEGATIVE_LONG_VALIDATOR); + VALIDATORS.put(Secure.DARK_THEME_CUSTOM_END_TIME, NONE_NEGATIVE_LONG_VALIDATOR); VALIDATORS.put(Secure.GLOBAL_ACTIONS_PANEL_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.AWARE_LOCK_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DISPLAY_DENSITY_FORCED, NON_NEGATIVE_INTEGER_VALIDATOR); diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java index 71c7544a3a4c..8d5c6e69b850 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java @@ -207,4 +207,15 @@ public class SettingsValidators { static final Validator ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR = new AccessibilityShortcutTargetListValidator(); + + static final Validator NONE_NEGATIVE_LONG_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + try { + return Long.parseLong(value) >= 0; + } catch (NumberFormatException e) { + return false; + } + } + }; } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 36bb8ef62320..b6e31d26a088 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -75,6 +75,9 @@ public class SettingsHelper { sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS); sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); sBroadcastOnRestore.add(Settings.Global.BLUETOOTH_ON); + sBroadcastOnRestore.add(Settings.Secure.UI_NIGHT_MODE); + sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_START_TIME); + sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME); } private interface SettingsLookup { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 2dc6f393f7cc..5a9d7497b641 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2778,6 +2778,11 @@ public class SettingsProvider extends ContentProvider { public boolean insertSettingLocked(int type, int userId, String name, String value, String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName, boolean forceNotify, Set<String> criticalSettings, boolean overrideableByRestore) { + if (overrideableByRestore != Settings.DEFAULT_OVERRIDEABLE_BY_RESTORE) { + getContext().enforceCallingOrSelfPermission( + Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE, + "Caller is not allowed to modify settings overrideable by restore"); + } final int key = makeKey(type, userId); boolean success = false; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index cd62420f39ac..2d351c74bf54 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -1259,7 +1259,8 @@ final class SettingsState { public boolean reset() { // overrideableByRestore = true as resetting to default value isn't considered a // modification. - return update(this.defaultValue, false, packageName, null, true, true); + return update(this.defaultValue, false, packageName, null, true, true, + /* resetToDefault */ true); } public boolean isTransient() { @@ -1272,6 +1273,13 @@ final class SettingsState { public boolean update(String value, boolean setDefault, String packageName, String tag, boolean forceNonSystemPackage, boolean overrideableByRestore) { + return update(value, setDefault, packageName, tag, forceNonSystemPackage, + overrideableByRestore, /* resetToDefault */ false); + } + + private boolean update(String value, boolean setDefault, String packageName, String tag, + boolean forceNonSystemPackage, boolean overrideableByRestore, + boolean resetToDefault) { if (NULL_VALUE.equals(value)) { value = null; } @@ -1305,7 +1313,7 @@ final class SettingsState { } // isValuePreservedInRestore shouldn't change back to false if it has been set to true. - boolean isPreserved = this.isValuePreservedInRestore || !overrideableByRestore; + boolean isPreserved = shouldPreserveSetting(overrideableByRestore, resetToDefault); // Is something gonna change? if (Objects.equals(value, this.value) @@ -1329,6 +1337,17 @@ final class SettingsState { + " packageName=" + packageName + " tag=" + tag + " defaultFromSystem=" + defaultFromSystem + "}"; } + + private boolean shouldPreserveSetting(boolean overrideableByRestore, + boolean resetToDefault) { + if (resetToDefault) { + // By default settings are not marked as preserved. + return false; + } + + // isValuePreservedInRestore shouldn't change back to false if it has been set to true. + return this.isValuePreservedInRestore || !overrideableByRestore; + } } /** diff --git a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java index bb9e6f6fe885..9134d875097e 100644 --- a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java +++ b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java @@ -277,6 +277,27 @@ public class SettingsValidatorsTest { } @Test + public void testPositiveLongValidator_zero() { + assertTrue(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("0")); + } + + @Test + public void testPositiveLongValidator_negative() { + assertFalse(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("-5")); + } + + + @Test + public void testPositiveLongValidator_positive() { + assertTrue(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("5")); + } + + @Test + public void testPositiveLongValidator_floatFormat() { + assertFalse(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("4.4756")); + } + + @Test public void testTTSListValidator_withNullInput_returnsFalse() { assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(null)); } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index b855d87fc214..6a3c6619c0ef 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -241,6 +241,18 @@ public class SettingsStateTest extends AndroidTestCase { assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); } + public void testResetSetting_preservedFlagIsReset() { + SettingsState settingsState = getSettingStateObject(); + // Initialize the setting. + settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE); + // Update the setting so that preserved flag is set. + settingsState.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE); + + settingsState.resetSettingLocked(SETTING_NAME); + assertFalse(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); + + } + private SettingsState getSettingStateObject() { SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 181e0c0790db..8f859b25faef 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -275,6 +275,9 @@ <!-- Permission needed to test registering pull atom callbacks --> <uses-permission android:name="android.permission.REGISTER_STATS_PULL_ATOM" /> + <!-- Permission needed to modify settings overrideable by restore in CTS tests --> + <uses-permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index cff958faa7e1..c036b041cd95 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -12,6 +12,9 @@ "include-annotation": "android.platform.test.scenario.annotation.Scenario" }, { + "exclude-annotation": "org.junit.Ignore" + }, + { "exclude-annotation": "androidx.test.filters.FlakyTest" } ] @@ -28,6 +31,9 @@ "include-annotation": "android.platform.test.scenario.annotation.Scenario" }, { + "exclude-annotation": "org.junit.Ignore" + }, + { "exclude-annotation": "androidx.test.filters.FlakyTest" }, { diff --git a/packages/SystemUI/res/color/control_background.xml b/packages/SystemUI/res/color/control_background.xml deleted file mode 100644 index 977310cfae01..000000000000 --- a/packages/SystemUI/res/color/control_background.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_enabled="false" - android:color="@color/control_default_background" /> - <item android:color="@color/GM2_blue_200" - android:alpha="0.2" /> -</selector> diff --git a/packages/SystemUI/res/color/light_background.xml b/packages/SystemUI/res/color/light_background.xml deleted file mode 100644 index 12994646a346..000000000000 --- a/packages/SystemUI/res/color/light_background.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_enabled="false" - android:color="@color/control_default_background" /> - <item android:color="@color/GM2_yellow_200" - android:alpha="0.2" /> -</selector> diff --git a/packages/SystemUI/res/color/thermo_cool_background.xml b/packages/SystemUI/res/color/thermo_cool_background.xml deleted file mode 100644 index 977310cfae01..000000000000 --- a/packages/SystemUI/res/color/thermo_cool_background.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_enabled="false" - android:color="@color/control_default_background" /> - <item android:color="@color/GM2_blue_200" - android:alpha="0.2" /> -</selector> diff --git a/packages/SystemUI/res/color/thermo_heat_background.xml b/packages/SystemUI/res/color/thermo_heat_background.xml deleted file mode 100644 index 2709ebe4dcfd..000000000000 --- a/packages/SystemUI/res/color/thermo_heat_background.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_enabled="false" - android:color="@color/control_default_background" /> - <item android:color="@color/GM2_red_200" - android:alpha="0.2" /> -</selector> diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml index 374e3b120e4b..c58f572e1bbb 100644 --- a/packages/SystemUI/res/layout/controls_base_item.xml +++ b/packages/SystemUI/res/layout/controls_base_item.xml @@ -21,8 +21,9 @@ android:layout_weight="1" android:layout_height="@dimen/control_height" android:padding="@dimen/control_padding" - android:clickable="true" + android:clickable="false" android:focusable="true" + android:screenReaderFocusable="true" android:layout_marginLeft="@dimen/control_base_item_margin" android:layout_marginRight="@dimen/control_base_item_margin" android:background="@drawable/control_background"> @@ -32,6 +33,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingTop="@dimen/control_padding_adjustment" + android:clickable="false" + android:focusable="false" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -42,6 +45,8 @@ android:textAppearance="@style/TextAppearance.Control.Status" android:paddingTop="@dimen/control_padding_adjustment" android:paddingStart="@dimen/control_status_padding" + android:clickable="false" + android:focusable="false" app:layout_constraintBottom_toBottomOf="@+id/icon" app:layout_constraintStart_toEndOf="@+id/icon" /> @@ -52,6 +57,8 @@ android:textAppearance="@style/TextAppearance.Control.Status" android:paddingTop="@dimen/control_padding_adjustment" android:paddingStart="@dimen/control_status_padding" + android:clickable="false" + android:focusable="false" app:layout_constraintBottom_toBottomOf="@+id/icon" app:layout_constraintStart_toEndOf="@+id/status" /> @@ -62,6 +69,8 @@ android:textAppearance="@style/TextAppearance.Control.Title" android:paddingLeft="@dimen/control_padding_adjustment" android:paddingRight="@dimen/control_padding_adjustment" + android:clickable="false" + android:focusable="false" app:layout_constraintBottom_toTopOf="@+id/subtitle" app:layout_constraintStart_toStartOf="parent" /> @@ -73,6 +82,8 @@ android:paddingLeft="@dimen/control_padding_adjustment" android:paddingRight="@dimen/control_padding_adjustment" android:paddingBottom="@dimen/control_padding_adjustment" + android:clickable="false" + android:focusable="false" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"/> diff --git a/packages/SystemUI/res/layout/controls_onboarding.xml b/packages/SystemUI/res/layout/controls_onboarding.xml new file mode 100644 index 000000000000..577a3b404472 --- /dev/null +++ b/packages/SystemUI/res/layout/controls_onboarding.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:padding="4dp" + android:orientation="vertical"> + + <View + android:id="@+id/arrow" + android:elevation="2dp" + android:layout_width="10dp" + android:layout_height="8dp" + android:layout_marginBottom="-2dp" + android:layout_gravity="center_horizontal"/> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingStart="24dp" + android:paddingEnd="4dp" + android:background="@drawable/recents_onboarding_toast_rounded_background" + android:layout_gravity="center_horizontal" + android:elevation="2dp" + android:orientation="horizontal"> + + <TextView + android:id="@+id/onboarding_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_gravity="center_vertical" + android:textColor="?attr/wallpaperTextColor" + android:textSize="16sp"/> + <ImageView + android:id="@+id/dismiss" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_gravity="center_vertical" + android:padding="10dp" + android:layout_marginStart="2dp" + android:layout_marginEnd="2dp" + android:alpha="0.7" + android:src="@drawable/ic_close_white" + android:tint="?attr/wallpaperTextColor" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/accessibility_desc_close"/> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/controls_structure_page.xml b/packages/SystemUI/res/layout/controls_structure_page.xml index 2c7e1681f2e1..047ab98eb191 100644 --- a/packages/SystemUI/res/layout/controls_structure_page.xml +++ b/packages/SystemUI/res/layout/controls_structure_page.xml @@ -15,17 +15,10 @@ ~ limitations under the License. --> -<androidx.core.widget.NestedScrollView +<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/listAll" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:layout_marginTop="@dimen/controls_management_list_margin"> - - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/listAll" - android:layout_width="match_parent" - android:layout_height="match_parent" - /> - -</androidx.core.widget.NestedScrollView>
\ No newline at end of file + android:layout_marginTop="@dimen/controls_management_list_margin"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 80c1ac8e3496..56e2b061ebc2 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -225,4 +225,8 @@ <color name="control_default_background">@color/GM2_grey_900</color> <color name="control_list_popup_background">@*android:color/background_floating_material_dark</color> <color name="control_spinner_dropdown">@*android:color/foreground_material_dark</color> + <color name="control_enabled_light_background">@color/GM2_yellow_200</color> + <color name="control_enabled_thermo_heat_background">@color/GM2_red_200</color> + <color name="control_enabled_thermo_cool_background">@color/GM2_blue_200</color> + <color name="control_enabled_default_background">@color/GM2_blue_200</color> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index d16082915207..217abbe3778a 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -117,7 +117,7 @@ <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> - wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,controls,screenrecord + wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord </string> <!-- The tiles to display in QuickSettings --> @@ -468,6 +468,9 @@ <!-- On debuggable builds, alert the user if SystemUI PSS goes over this number (in kb) --> <integer name="watch_heap_limit">256000</integer> + <!-- Animation duration for resizing of PIP when entering/exiting. --> + <integer name="config_pipResizeAnimationDuration">425</integer> + <!-- Allow dragging the PIP to a location to close it --> <bool name="config_pipEnableDismissDragToEdge">true</bool> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 291db65da225..e45cbecd3aa1 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -124,9 +124,6 @@ <!-- Increased height of a collapsed media notification in the status bar --> <dimen name="notification_min_height_media">160dp</dimen> - <!-- Increased height of a collapsed messaging notification in the status bar --> - <dimen name="notification_min_height_messaging">118dp</dimen> - <!-- Height of a small notification in the status bar which was used before android N --> <dimen name="notification_min_height_legacy">64dp</dimen> @@ -1234,7 +1231,7 @@ <dimen name="control_height">106dp</dimen> <dimen name="control_padding">12dp</dimen> <dimen name="control_padding_adjustment">4dp</dimen> - <dimen name="control_status_normal">12sp</dimen> + <dimen name="control_status_normal">14sp</dimen> <dimen name="control_status_expanded">18sp</dimen> <dimen name="control_base_item_margin">2dp</dimen> <dimen name="control_status_padding">3dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index caf22fe16beb..35430733a118 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2631,10 +2631,7 @@ <string name="controls_favorite_default_title">Controls</string> <!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] --> <string name="controls_favorite_subtitle">Choose controls for quick access</string> - <!-- Controls management controls screen favorites header [CHAR LIMIT=50] --> - <string name="controls_favorite_header_favorites">Favorites</string> - <!-- Controls management controls screen all header [CHAR LIMIT=50] --> - <string name="controls_favorite_header_all">All</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 header for Other zone [CHAR LIMIT=60] --> @@ -2653,4 +2650,7 @@ <string name="controls_pin_verify">Verify device PIN</string> <!-- Controls PIN entry dialog, text hint [CHAR LIMIT=30] --> <string name="controls_pin_instructions">Enter PIN</string> + + <!-- Tooltip to show in management screen when there are multiple structures [CHAR_LIMIT=50] --> + <string name="controls_structure_tooltip">Swipe to see other structures</string> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 125dd8f1d60c..20b88a1a550d 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -650,6 +650,7 @@ <!-- Controls styles --> <style name="Theme.ControlsManagement" parent="@android:style/Theme.DeviceDefault.NoActionBar"> <item name="android:windowIsTranslucent">false</item> + <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item> </style> <style name="TextAppearance.Control"> @@ -676,7 +677,7 @@ <style name="TextAppearance.Control.Status"> <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> - <item name="android:textSize">@dimen/control_text_size</item> + <item name="android:textSize">@dimen/control_status_normal</item> <item name="android:textColor">@color/control_primary_text</item> </style> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index 1c6223b847de..9e9b9dc4f3b0 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -37,7 +37,8 @@ public abstract class TaskStackChangeListener { public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) { } public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { } public void onActivityUnpinned() { } - public void onPinnedActivityRestartAttempt(boolean clearedTask) { } + public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) { } public void onActivityForcedResizable(String packageName, int taskId, int reason) { } public void onActivityDismissingDockedStack() { } public void onActivityLaunchOnSecondaryDisplayFailed() { } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index cbdd3f8191f4..ce9cbabaa5e9 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -30,6 +30,7 @@ import android.os.RemoteException; import android.os.Trace; import android.util.Log; +import com.android.internal.os.SomeArgs; import com.android.systemui.shared.recents.model.ThumbnailData; import java.util.ArrayList; @@ -120,11 +121,14 @@ public class TaskStackChangeListeners extends TaskStackListener { } @Override - public void onPinnedActivityRestartAttempt(boolean clearedTask) - throws RemoteException { - mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT); - mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0) - .sendToTarget(); + public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) throws RemoteException { + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = task; + args.argi1 = homeTaskVisible ? 1 : 0; + args.argi2 = clearedTask ? 1 : 0; + mHandler.removeMessages(H.ON_ACTIVITY_RESTART_ATTEMPT); + mHandler.obtainMessage(H.ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget(); } @Override @@ -236,7 +240,7 @@ public class TaskStackChangeListeners extends TaskStackListener { private static final int ON_TASK_STACK_CHANGED = 1; private static final int ON_TASK_SNAPSHOT_CHANGED = 2; private static final int ON_ACTIVITY_PINNED = 3; - private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4; + private static final int ON_ACTIVITY_RESTART_ATTEMPT = 4; private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6; private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7; private static final int ON_TASK_PROFILE_LOCKED = 8; @@ -296,10 +300,14 @@ public class TaskStackChangeListeners extends TaskStackListener { } break; } - case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: { + case ON_ACTIVITY_RESTART_ATTEMPT: { + final SomeArgs args = (SomeArgs) msg.obj; + final RunningTaskInfo task = (RunningTaskInfo) args.arg1; + final boolean homeTaskVisible = args.argi1 != 0; + final boolean clearedTask = args.argi2 != 0; for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onPinnedActivityRestartAttempt( - msg.arg1 != 0); + mTaskStackListeners.get(i).onActivityRestartAttempt(task, + homeTaskVisible, clearedTask); } break; } @@ -419,6 +427,9 @@ public class TaskStackChangeListeners extends TaskStackListener { } } } + if (msg.obj instanceof SomeArgs) { + ((SomeArgs) msg.obj).recycle(); + } } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java index b813e2178fa3..7570c2cbfe98 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java @@ -18,7 +18,11 @@ package com.android.systemui.shared.system; import android.app.WallpaperManager; import android.content.Context; +import android.os.IBinder; +/** + * @see WallpaperManager + */ public class WallpaperManagerCompat { private final WallpaperManager mWallpaperManager; @@ -26,7 +30,10 @@ public class WallpaperManagerCompat { mWallpaperManager = context.getSystemService(WallpaperManager.class); } - public void setWallpaperZoomOut(float zoom) { - mWallpaperManager.setWallpaperZoomOut(zoom); + /** + * @see WallpaperManager#setWallpaperZoomOut(IBinder, float) + */ + public void setWallpaperZoomOut(IBinder windowToken, float zoom) { + mWallpaperManager.setWallpaperZoomOut(windowToken, zoom); } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 3cf07d14cbf2..ba8a1a945a77 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -19,9 +19,7 @@ import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.ViewRootImpl.sNewInsetsMode; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.systemBars; - import static com.android.systemui.DejankUtils.whitelistIpcs; - import static java.lang.Integer.max; import android.app.Activity; @@ -30,6 +28,7 @@ import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; +import android.graphics.Rect; import android.metrics.LogMaker; import android.os.Handler; import android.os.Looper; @@ -512,8 +511,6 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe boolean finish = false; boolean strongAuth = false; int eventSubtype = -1; - mCurrentSecuritySelection = whitelistIpcs(() -> - mSecurityModel.getSecurityMode(targetUserId)); if (mUpdateMonitor.getUserHasTrust(targetUserId)) { finish = true; eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS; @@ -521,8 +518,13 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe finish = true; eventSubtype = BOUNCER_DISMISS_BIOMETRIC; } else if (SecurityMode.None == mCurrentSecuritySelection) { - finish = true; // no security required - eventSubtype = BOUNCER_DISMISS_NONE_SECURITY; + SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); + if (SecurityMode.None == securityMode) { + finish = true; // no security required + eventSubtype = BOUNCER_DISMISS_NONE_SECURITY; + } else { + showSecurityScreen(securityMode); // switch to the alternate security view + } } else if (authenticated) { switch (mCurrentSecuritySelection) { case Pattern: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 4508fc74e884..431c4519bd81 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -379,7 +379,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (DEBUG_SIM_STATES) { Log.v(TAG, "onSubscriptionInfoChanged()"); List<SubscriptionInfo> sil = mSubscriptionManager - .getActiveAndHiddenSubscriptionInfoList(); + .getCompleteActiveSubscriptionInfoList(); if (sil != null) { for (SubscriptionInfo subInfo : sil) { Log.v(TAG, "SubInfo:" + subInfo); @@ -433,10 +433,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) { List<SubscriptionInfo> sil = mSubscriptionInfo; if (sil == null || forceReload) { - sil = mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList(); + sil = mSubscriptionManager.getCompleteActiveSubscriptionInfoList(); } if (sil == null) { - // getActiveAndHiddenSubscriptionInfoList was null callers expect an empty list. + // getCompleteActiveSubscriptionInfoList was null callers expect an empty list. mSubscriptionInfo = new ArrayList<SubscriptionInfo>(); } else { mSubscriptionInfo = sil; @@ -1086,7 +1086,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mHandler.sendEmptyMessage(MSG_TIME_UPDATE); } else if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { final Message msg = mHandler.obtainMessage( - MSG_TIMEZONE_UPDATE, intent.getStringExtra("time-zone")); + MSG_TIMEZONE_UPDATE, intent.getStringExtra(Intent.EXTRA_TIMEZONE)); mHandler.sendMessage(msg); } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index a868cf58cf7c..b6152dae33d6 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -71,12 +71,11 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment; import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; @@ -289,7 +288,6 @@ public class Dependency { @Inject Lazy<NotificationLogger> mNotificationLogger; @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager; @Inject Lazy<NotificationFilter> mNotificationFilter; - @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider; @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil; @Inject Lazy<SmartReplyController> mSmartReplyController; @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler; @@ -489,8 +487,6 @@ public class Dependency { mProviders.put(NotificationViewHierarchyManager.class, mNotificationViewHierarchyManager::get); mProviders.put(NotificationFilter.class, mNotificationFilter::get); - mProviders.put(NotificationInterruptionStateProvider.class, - mNotificationInterruptionStateProvider::get); mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get); mProviders.put(SmartReplyController.class, mSmartReplyController::get); mProviders.put(RemoteInputQuickSettingsDisabler.class, diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index dbcdead8de9f..23fa645eff16 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -203,6 +203,11 @@ public class ImageWallpaper extends WallpaperService { } } + @Override + public boolean shouldZoomOutWallpaper() { + return true; + } + private void waitForBackgroundRendering() { synchronized (mMonitor) { try { diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index 5e6589f76c13..6aa2326c388a 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -59,7 +59,8 @@ public final class Prefs { Key.TOUCHED_RINGER_TOGGLE, Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, Key.HAS_SEEN_BUBBLES_EDUCATION, - Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION + Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION, + Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT }) public @interface Key { @Deprecated @@ -107,6 +108,7 @@ public final class Prefs { String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip"; String HAS_SEEN_BUBBLES_EDUCATION = "HasSeenBubblesOnboarding"; String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding"; + String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount"; } public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) { diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index cab9f187da6f..537a812d19cb 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -48,6 +48,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Region; import android.hardware.display.DisplayManager; import android.os.Handler; @@ -725,7 +726,8 @@ public class ScreenDecorations extends SystemUI implements Tunable { private final Rect mBoundingRect = new Rect(); private final Path mBoundingPath = new Path(); // Don't initialize these yet because they may never exist - private Rect mProtectionRect; + private RectF mProtectionRect; + private RectF mProtectionRectOrig; private Path mProtectionPath; private Path mProtectionPathOrig; private Rect mTotalBounds = new Rect(); @@ -818,7 +820,11 @@ public class ScreenDecorations extends SystemUI implements Tunable { mProtectionPath = new Path(); } mProtectionPathOrig.set(protectionPath); - mProtectionRect = pathBounds; + if (mProtectionRectOrig == null) { + mProtectionRectOrig = new RectF(); + mProtectionRect = new RectF(); + } + mProtectionRectOrig.set(pathBounds); } void setShowProtection(boolean shouldShow) { @@ -898,6 +904,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { // Reset the protection path so we don't aggregate rotations mProtectionPath.set(mProtectionPathOrig); mProtectionPath.transform(m); + m.mapRect(mProtectionRect, mProtectionRectOrig); } } @@ -964,7 +971,8 @@ public class ScreenDecorations extends SystemUI implements Tunable { if (mShowProtection) { // Make sure that our measured height encompases the protection mTotalBounds.union(mBoundingRect); - mTotalBounds.union(mProtectionRect); + mTotalBounds.union((int) mProtectionRect.left, (int) mProtectionRect.top, + (int) mProtectionRect.right, (int) mProtectionRect.bottom); setMeasuredDimension( resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0), resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0)); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 22c2c7ee9750..406e7cef9bfa 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -83,11 +83,11 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationRemoveInterceptor; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; @@ -169,7 +169,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // Callback that updates BubbleOverflowActivity on data change. @Nullable private Runnable mOverflowCallback = null; - private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; + private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private IStatusBarService mBarService; // Used for determining view rect for touch interaction @@ -279,7 +279,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi ShadeController shadeController, BubbleData data, ConfigurationController configurationController, - NotificationInterruptionStateProvider interruptionStateProvider, + NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManager groupManager, @@ -304,7 +304,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, ConfigurationController configurationController, - NotificationInterruptionStateProvider interruptionStateProvider, + NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManager groupManager, @@ -316,7 +316,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi dumpManager.registerDumpable(TAG, this); mContext = context; mShadeController = shadeController; - mNotificationInterruptionStateProvider = interruptionStateProvider; + mNotificationInterruptStateProvider = interruptionStateProvider; mNotifUserManager = notifUserManager; mZenModeController = zenModeController; mFloatingContentCoordinator = floatingContentCoordinator; @@ -632,7 +632,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi for (NotificationEntry e : mNotificationEntryManager.getActiveNotificationsForCurrentUser()) { if (savedBubbleKeys.contains(e.getKey()) - && mNotificationInterruptionStateProvider.shouldBubbleUp(e) + && mNotificationInterruptStateProvider.shouldBubbleUp(e) && canLaunchInActivityView(mContext, e)) { updateBubble(e, /* suppressFlyout= */ true); } @@ -894,7 +894,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments( mContext, entry, previouslyUserCreated, userBlocked); - if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry) + if (mNotificationInterruptStateProvider.shouldBubbleUp(entry) && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) { if (wasAdjusted && !previouslyUserCreated) { // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated @@ -910,7 +910,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments( mContext, entry, previouslyUserCreated, userBlocked); - boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry) + boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry) && (canLaunchInActivityView(mContext, entry) || wasAdjusted); if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it @@ -1227,6 +1227,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } @Override + public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) { + for (Bubble b : mBubbleData.getBubbles()) { + if (b.getDisplayId() == task.displayId) { + expandStackAndSelectBubble(b.getKey()); + return; + } + } + } + + @Override public void onActivityLaunchOnSecondaryDisplayRerouted() { if (mStackView != null) { mBubbleData.setExpanded(false); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index aedd2db738ee..0778d5b54493 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -424,6 +424,7 @@ public class BubbleStackView extends FrameLayout { private int mOrientation = Configuration.ORIENTATION_UNDEFINED; + @Nullable private BubbleOverflow mBubbleOverflow; private boolean mShouldShowUserEducation; @@ -1386,6 +1387,11 @@ public class BubbleStackView extends FrameLayout { if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "onBubbleDragStart: bubble=" + bubble); } + + if (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView())) { + return; + } + mExpandedAnimationController.prepareForBubbleDrag(bubble, mMagneticTarget); // We're dragging an individual bubble, so set the magnetized object to the magnetized @@ -1398,7 +1404,8 @@ public class BubbleStackView extends FrameLayout { /** Called with the coordinates to which an individual bubble has been dragged. */ public void onBubbleDragged(View bubble, float x, float y) { - if (!mIsExpanded || mIsExpansionAnimating) { + if (!mIsExpanded || mIsExpansionAnimating + || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) { return; } @@ -1413,7 +1420,8 @@ public class BubbleStackView extends FrameLayout { Log.d(TAG, "onBubbleDragFinish: bubble=" + bubble); } - if (!mIsExpanded || mIsExpansionAnimating) { + if (!mIsExpanded || mIsExpansionAnimating + || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) { return; } @@ -1421,6 +1429,18 @@ public class BubbleStackView extends FrameLayout { hideDismissTarget(); } + /** Expands the clicked bubble. */ + public void expandBubble(Bubble bubble) { + if (bubble.equals(mBubbleData.getSelectedBubble())) { + // If the bubble we're supposed to expand is the selected bubble, that means the + // overflow bubble is currently expanded. Don't tell BubbleData to set this bubble as + // selected, since it already is. Just call the stack's setSelectedBubble to expand it. + setSelectedBubble(bubble); + } else { + mBubbleData.setSelectedBubble(bubble); + } + } + void onDragStart() { if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "onDragStart()"); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java index 0c5bef4d2bde..132c45fab3d2 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java @@ -189,7 +189,7 @@ class BubbleTouchHandler implements View.OnTouchListener { if (key == BubbleOverflow.KEY) { mStack.showOverflow(); } else { - mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key)); + mStack.expandBubble(mBubbleData.getBubbleWithKey(key)); } } resetForNextGesture(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java index ac97d8aab326..27c9e9895324 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java @@ -25,8 +25,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; @@ -54,7 +54,7 @@ public interface BubbleModule { ShadeController shadeController, BubbleData data, ConfigurationController configurationController, - NotificationInterruptionStateProvider interruptionStateProvider, + NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManager groupManager, diff --git a/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt b/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt new file mode 100644 index 000000000000..6e17bc94b16b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls + +import android.annotation.StringRes +import android.content.Context +import android.graphics.CornerPathEffect +import android.graphics.drawable.ShapeDrawable +import android.util.TypedValue +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator +import android.widget.TextView +import com.android.systemui.Prefs +import com.android.systemui.R +import com.android.systemui.recents.TriangleShape + +/** + * Manager for showing an onboarding tooltip on screen. + * + * The tooltip can be made to appear below or above a point. The number of times it will appear + * is determined by an shared preference (defined in [Prefs]). + * + * @property context A context to use to inflate the views and retrieve shared preferences from + * @property preferenceName name of the preference to use to track the number of times the tooltip + * has been shown. + * @property maxTimesShown the maximum number of times to show the tooltip + * @property below whether the tooltip should appear below (with up pointing arrow) or above (down + * pointing arrow) the specified point. + * @see [TooltipManager.show] + */ +class TooltipManager( + context: Context, + private val preferenceName: String, + private val maxTimesShown: Int = 2, + private val below: Boolean = true +) { + + companion object { + private const val SHOW_DELAY_MS: Long = 500 + private const val SHOW_DURATION_MS: Long = 300 + private const val HIDE_DURATION_MS: Long = 100 + } + + private var shown = Prefs.getInt(context, preferenceName, 0) + + val layout: ViewGroup = + LayoutInflater.from(context).inflate(R.layout.controls_onboarding, null) as ViewGroup + val preferenceStorer = { num: Int -> + Prefs.putInt(context, preferenceName, num) + } + + init { + layout.alpha = 0f + } + + private val textView = layout.requireViewById<TextView>(R.id.onboarding_text) + private val dismissView = layout.requireViewById<View>(R.id.dismiss).apply { + setOnClickListener { + hide(true) + } + } + + private val arrowView = layout.requireViewById<View>(R.id.arrow).apply { + val typedValue = TypedValue() + context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true) + val toastColor = context.resources.getColor(typedValue.resourceId, context.theme) + val arrowRadius = context.resources.getDimensionPixelSize( + R.dimen.recents_onboarding_toast_arrow_corner_radius) + val arrowLp = layoutParams + val arrowDrawable = ShapeDrawable(TriangleShape.create( + arrowLp.width.toFloat(), arrowLp.height.toFloat(), below)) + val arrowPaint = arrowDrawable.paint + arrowPaint.color = toastColor + // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable. + arrowPaint.pathEffect = CornerPathEffect(arrowRadius.toFloat()) + setBackground(arrowDrawable) + } + + init { + if (!below) { + layout.removeView(arrowView) + layout.addView(arrowView) + (arrowView.layoutParams as ViewGroup.MarginLayoutParams).apply { + bottomMargin = topMargin + topMargin = 0 + } + } + } + + /** + * Show the tooltip + * + * @param stringRes the id of the string to show in the tooltip + * @param x horizontal position (w.r.t. screen) for the arrow point + * @param y vertical position (w.r.t. screen) for the arrow point + */ + fun show(@StringRes stringRes: Int, x: Int, y: Int) { + if (!shouldShow()) return + textView.setText(stringRes) + shown++ + preferenceStorer(shown) + layout.post { + val p = IntArray(2) + layout.getLocationOnScreen(p) + layout.translationX = (x - p[0] - layout.width / 2).toFloat() + layout.translationY = (y - p[1]).toFloat() - if (!below) layout.height else 0 + if (layout.alpha == 0f) { + layout.animate() + .alpha(1f) + .withLayer() + .setStartDelay(SHOW_DELAY_MS) + .setDuration(SHOW_DURATION_MS) + .setInterpolator(DecelerateInterpolator()) + .start() + } + } + } + + /** + * Hide the tooltip + * + * @param animate whether to animate the fade out + */ + fun hide(animate: Boolean = false) { + if (layout.alpha == 0f) return + layout.post { + if (animate) { + layout.animate() + .alpha(0f) + .withLayer() + .setStartDelay(0) + .setDuration(HIDE_DURATION_MS) + .setInterpolator(AccelerateInterpolator()) + .start() + } else { + layout.animate().cancel() + layout.alpha = 0f + } + } + } + + private fun shouldShow() = shown < maxTimesShown +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt index fd6e2566b1b6..c5af436e7e92 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt @@ -38,8 +38,9 @@ interface ControlsBindingController : UserAwareController { * * @param component The [ComponentName] of the service to bind * @param callback a callback to return the loaded controls to (or an error). + * @return a runnable to cancel the load */ - fun bindAndLoad(component: ComponentName, callback: LoadCallback) + fun bindAndLoad(component: ComponentName, callback: LoadCallback): Runnable /** * Request to bind to the given service. diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt index 8f02c252beb1..f8d4a39a98fe 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt @@ -31,7 +31,6 @@ import com.android.internal.annotations.VisibleForTesting import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.concurrency.DelayableExecutor import dagger.Lazy -import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject import javax.inject.Singleton @@ -47,8 +46,6 @@ open class ControlsBindingControllerImpl @Inject constructor( private const val TAG = "ControlsBindingControllerImpl" } - private val refreshing = AtomicBoolean(false) - private var currentUser = UserHandle.of(ActivityManager.getCurrentUser()) override val currentUserId: Int @@ -56,6 +53,12 @@ open class ControlsBindingControllerImpl @Inject constructor( private var currentProvider: ControlsProviderLifecycleManager? = null + /* + * Will track any active subscriber for subscribe/unsubscribe requests coming into + * this controller. Only one can be active at any time + */ + private var statefulControlSubscriber: StatefulControlSubscriber? = null + private val actionCallbackService = object : IControlsActionCallback.Stub() { override fun accept( token: IBinder, @@ -66,27 +69,6 @@ open class ControlsBindingControllerImpl @Inject constructor( } } - private val subscriberService = object : IControlsSubscriber.Stub() { - override fun onSubscribe(token: IBinder, subs: IControlsSubscription) { - backgroundExecutor.execute(OnSubscribeRunnable(token, subs)) - } - - override fun onNext(token: IBinder, c: Control) { - if (!refreshing.get()) { - Log.d(TAG, "Refresh outside of window for token:$token") - } else { - backgroundExecutor.execute(OnNextRunnable(token, c)) - } - } - override fun onError(token: IBinder, s: String) { - backgroundExecutor.execute(OnErrorRunnable(token, s)) - } - - override fun onComplete(token: IBinder) { - backgroundExecutor.execute(OnCompleteRunnable(token)) - } - } - @VisibleForTesting internal open fun createProviderManager(component: ComponentName): ControlsProviderLifecycleManager { @@ -94,43 +76,45 @@ open class ControlsBindingControllerImpl @Inject constructor( context, backgroundExecutor, actionCallbackService, - subscriberService, currentUser, component ) } private fun retrieveLifecycleManager(component: ComponentName): - ControlsProviderLifecycleManager? { + ControlsProviderLifecycleManager { if (currentProvider != null && currentProvider?.componentName != component) { unbind() } - if (currentProvider == null) { - currentProvider = createProviderManager(component) - } + val provider = currentProvider ?: createProviderManager(component) + currentProvider = provider - return currentProvider + return provider } override fun bindAndLoad( component: ComponentName, callback: ControlsBindingController.LoadCallback - ) { - retrieveLifecycleManager(component)?.maybeBindAndLoad(LoadSubscriber(callback)) + ): Runnable { + val subscriber = LoadSubscriber(callback) + retrieveLifecycleManager(component).maybeBindAndLoad(subscriber) + return subscriber.loadCancel() } override fun subscribe(structureInfo: StructureInfo) { - if (refreshing.compareAndSet(false, true)) { - val provider = retrieveLifecycleManager(structureInfo.componentName) - provider?.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId }) - } + // make sure this has happened. only allow one active subscription + unsubscribe() + + statefulControlSubscriber = null + val provider = retrieveLifecycleManager(structureInfo.componentName) + val scs = StatefulControlSubscriber(lazyController.get(), provider, backgroundExecutor) + statefulControlSubscriber = scs + provider.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId }, scs) } override fun unsubscribe() { - if (refreshing.compareAndSet(true, false)) { - currentProvider?.unsubscribe() - } + statefulControlSubscriber?.cancel() } override fun action( @@ -138,20 +122,24 @@ open class ControlsBindingControllerImpl @Inject constructor( controlInfo: ControlInfo, action: ControlAction ) { - retrieveLifecycleManager(componentName) - ?.maybeBindAndSendAction(controlInfo.controlId, action) + if (statefulControlSubscriber == null) { + Log.w(TAG, "No actions can occur outside of an active subscription. Ignoring.") + } else { + retrieveLifecycleManager(componentName) + .maybeBindAndSendAction(controlInfo.controlId, action) + } } override fun bindService(component: ComponentName) { - retrieveLifecycleManager(component)?.bindService() + retrieveLifecycleManager(component).bindService() } override fun changeUser(newUser: UserHandle) { if (newUser == currentUser) return + unsubscribe() unbind() - - refreshing.set(false) + currentProvider = null currentUser = newUser } @@ -172,8 +160,8 @@ open class ControlsBindingControllerImpl @Inject constructor( override fun toString(): String { return StringBuilder(" ControlsBindingController:\n").apply { - append(" refreshing=${refreshing.get()}\n") append(" currentUser=$currentUser\n") + append(" StatefulControlSubscriber=$statefulControlSubscriber") append(" Providers=$currentProvider\n") }.toString() } @@ -208,22 +196,6 @@ open class ControlsBindingControllerImpl @Inject constructor( ) : CallbackRunnable(token) { override fun doRun() { callback.accept(list) - provider?.unbindService() - } - } - - private inner class OnNextRunnable( - token: IBinder, - val control: Control - ) : CallbackRunnable(token) { - override fun doRun() { - if (!refreshing.get()) { - Log.d(TAG, "onRefresh outside of window from:${provider?.componentName}") - } - - provider?.let { - lazyController.get().refreshStatus(it.componentName, control) - } } } @@ -232,33 +204,7 @@ open class ControlsBindingControllerImpl @Inject constructor( val subscription: IControlsSubscription ) : CallbackRunnable(token) { override fun doRun() { - if (!refreshing.get()) { - Log.d(TAG, "onRefresh outside of window from '${provider?.componentName}'") - } - provider?.let { - it.startSubscription(subscription) - } - } - } - - private inner class OnCompleteRunnable( - token: IBinder - ) : CallbackRunnable(token) { - override fun doRun() { - provider?.let { - Log.i(TAG, "onComplete receive from '${it.componentName}'") - } - } - } - - private inner class OnErrorRunnable( - token: IBinder, - val error: String - ) : CallbackRunnable(token) { - override fun doRun() { - provider?.let { - Log.e(TAG, "onError receive from '${it.componentName}': $error") - } + provider?.startSubscription(subscription) } } @@ -292,8 +238,14 @@ open class ControlsBindingControllerImpl @Inject constructor( ) : IControlsSubscriber.Stub() { val loadedControls = ArrayList<Control>() var hasError = false + private var _loadCancelInternal: (() -> Unit)? = null + fun loadCancel() = Runnable { + Log.d(TAG, "Cancel load requested") + _loadCancelInternal?.invoke() + } override fun onSubscribe(token: IBinder, subs: IControlsSubscription) { + _loadCancelInternal = subs::cancel backgroundExecutor.execute(OnSubscribeRunnable(token, subs)) } @@ -302,11 +254,15 @@ open class ControlsBindingControllerImpl @Inject constructor( } override fun onError(token: IBinder, s: String) { hasError = true + _loadCancelInternal = {} + currentProvider?.cancelLoadTimeout() backgroundExecutor.execute(OnLoadErrorRunnable(token, s, callback)) } override fun onComplete(token: IBinder) { + _loadCancelInternal = {} if (!hasError) { + currentProvider?.cancelLoadTimeout() backgroundExecutor.execute(OnLoadRunnable(token, loadedControls, callback)) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt index 7eafe2e65cca..9e0d26c3935f 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt @@ -59,6 +59,11 @@ interface ControlsController : UserAwareController { ) /** + * Cancels a pending load call + */ + fun cancelLoad() + + /** * Request to subscribe for favorited controls per structure * * @param structureInfo structure to limit the subscription to diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index e5d36f942ac8..9cb902f51f22 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -72,6 +72,8 @@ class ControlsControllerImpl @Inject constructor ( private var userChanging: Boolean = true + private var loadCanceller: Runnable? = null + private var currentUser = UserHandle.of(ActivityManager.getCurrentUser()) override val currentUserId get() = currentUser.identifier @@ -213,8 +215,9 @@ class ControlsControllerImpl @Inject constructor ( if (!confirmAvailability()) { if (userChanging) { // Try again later, userChanging should not last forever. If so, we have bigger - // problems - executor.executeDelayed( + // problems. This will return a runnable that allows to cancel the delayed version, + // it will not be able to cancel the load if + loadCanceller = executor.executeDelayed( { loadForComponent(componentName, dataCallback) }, USER_CHANGE_RETRY_DELAY, TimeUnit.MILLISECONDS @@ -224,10 +227,11 @@ class ControlsControllerImpl @Inject constructor ( } return } - bindingController.bindAndLoad( + loadCanceller = bindingController.bindAndLoad( componentName, object : ControlsBindingController.LoadCallback { override fun accept(controls: List<Control>) { + loadCanceller = null executor.execute { val favoritesForComponentKeys = Favorites .getControlsForComponent(componentName).map { it.controlId } @@ -251,12 +255,12 @@ class ControlsControllerImpl @Inject constructor ( controlsWithFavorite, favoritesForComponentKeys ) - dataCallback.accept(loadData) } } override fun error(message: String) { + loadCanceller = null executor.execute { val loadData = Favorites.getControlsForComponent(componentName) .let { controls -> @@ -269,7 +273,6 @@ class ControlsControllerImpl @Inject constructor ( true ) } - dataCallback.accept(loadData) } } @@ -277,6 +280,12 @@ class ControlsControllerImpl @Inject constructor ( ) } + override fun cancelLoad() { + loadCanceller?.let { + executor.execute(it) + } + } + private fun createRemovedStatus( componentName: ComponentName, controlInfo: ControlInfo, diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt index 86e8e834faf1..4918bd7b3349 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt @@ -58,7 +58,6 @@ class ControlsProviderLifecycleManager( private val context: Context, private val executor: DelayableExecutor, private val actionCallbackService: IControlsActionCallback.Stub, - private val subscriberService: IControlsSubscriber.Stub, val user: UserHandle, val componentName: ComponentName ) : IBinder.DeathRecipient { @@ -157,10 +156,9 @@ class ControlsProviderLifecycleManager( load(msg.subscriber) } - queue.filter { it is Message.Subscribe }.flatMap { (it as Message.Subscribe).list }.run { - if (this.isNotEmpty()) { - subscribe(this) - } + queue.filter { it is Message.Subscribe }.forEach { + val msg = it as Message.Subscribe + subscribe(msg.list, msg.subscriber) } queue.filter { it is Message.Action }.forEach { val msg = it as Message.Action @@ -185,9 +183,9 @@ class ControlsProviderLifecycleManager( } } - private fun unqueueMessage(message: Message) { + private fun unqueueMessageType(type: Int) { synchronized(queuedMessages) { - queuedMessages.removeIf { it.type == message.type } + queuedMessages.removeIf { it.type == type } } } @@ -219,7 +217,7 @@ class ControlsProviderLifecycleManager( * @param subscriber the subscriber that manages coordination for loading controls */ fun maybeBindAndLoad(subscriber: IControlsSubscriber.Stub) { - unqueueMessage(Message.Unbind) + unqueueMessageType(MSG_UNBIND) onLoadCanceller = executor.executeDelayed({ // Didn't receive a response in time, log and send back error Log.d(TAG, "Timeout waiting onLoad for $componentName") @@ -230,6 +228,11 @@ class ControlsProviderLifecycleManager( invokeOrQueue({ load(subscriber) }, Message.Load(subscriber)) } + fun cancelLoadTimeout() { + onLoadCanceller?.run() + onLoadCanceller = null + } + /** * Request a subscription to the [Publisher] returned by [ControlsProviderService.publisherFor] * @@ -237,16 +240,20 @@ class ControlsProviderLifecycleManager( * * @param controlIds a list of the ids of controls to send status back. */ - fun maybeBindAndSubscribe(controlIds: List<String>) { - invokeOrQueue({ subscribe(controlIds) }, Message.Subscribe(controlIds)) + fun maybeBindAndSubscribe(controlIds: List<String>, subscriber: IControlsSubscriber) { + invokeOrQueue( + { subscribe(controlIds, subscriber) }, + Message.Subscribe(controlIds, subscriber) + ) } - private fun subscribe(controlIds: List<String>) { + private fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber) { if (DEBUG) { Log.d(TAG, "subscribe $componentName - $controlIds") } - if (!(wrapper?.subscribe(controlIds, subscriberService) ?: false)) { - queueMessage(Message.Subscribe(controlIds)) + + if (!(wrapper?.subscribe(controlIds, subscriber) ?: false)) { + queueMessage(Message.Subscribe(controlIds, subscriber)) binderDied() } } @@ -276,10 +283,13 @@ class ControlsProviderLifecycleManager( /** * Starts the subscription to the [ControlsProviderService] and requests status of controls. * - * @param subscription the subscriber to use to request controls + * @param subscription the subscription to use to request controls * @see maybeBindAndLoad */ fun startSubscription(subscription: IControlsSubscription) { + if (DEBUG) { + Log.d(TAG, "startSubscription: $subscription") + } synchronized(subscriptions) { subscriptions.add(subscription) } @@ -287,30 +297,26 @@ class ControlsProviderLifecycleManager( } /** - * Unsubscribe from this service, cancelling all status requests. + * Cancels the subscription to the [ControlsProviderService]. + * + * @param subscription the subscription to cancel + * @see maybeBindAndLoad */ - fun unsubscribe() { + fun cancelSubscription(subscription: IControlsSubscription) { if (DEBUG) { - Log.d(TAG, "unsubscribe $componentName") + Log.d(TAG, "cancelSubscription: $subscription") } - unqueueMessage(Message.Subscribe(emptyList())) // Removes all subscribe messages - - val subs = synchronized(subscriptions) { - ArrayList(subscriptions).also { - subscriptions.clear() - } - } - - subs.forEach { - wrapper?.cancel(it) + synchronized(subscriptions) { + subscriptions.remove(subscription) } + wrapper?.cancel(subscription) } /** * Request bind to the service. */ fun bindService() { - unqueueMessage(Message.Unbind) + unqueueMessageType(MSG_UNBIND) bindService(true) } @@ -321,8 +327,16 @@ class ControlsProviderLifecycleManager( onLoadCanceller?.run() onLoadCanceller = null - // just in case this wasn't called already - unsubscribe() + // be sure to cancel all subscriptions + val subs = synchronized(subscriptions) { + ArrayList(subscriptions).also { + subscriptions.clear() + } + } + + subs.forEach { + wrapper?.cancel(it) + } bindService(false) } @@ -346,7 +360,7 @@ class ControlsProviderLifecycleManager( object Unbind : Message() { override val type = MSG_UNBIND } - class Subscribe(val list: List<String>) : Message() { + class Subscribe(val list: List<String>, val subscriber: IControlsSubscriber) : Message() { override val type = MSG_SUBSCRIBE } class Action(val id: String, val action: ControlAction) : Message() { diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt new file mode 100644 index 000000000000..a371aa64df6e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.controller + +import android.os.IBinder +import android.service.controls.Control +import android.service.controls.IControlsSubscriber +import android.service.controls.IControlsSubscription +import android.util.Log +import com.android.systemui.util.concurrency.DelayableExecutor + +/** + * A single subscriber, supporting stateful controls for publishers created by + * {@link ControlsProviderService#createPublisherFor}. In general, this subscription will remain + * active until the SysUi chooses to cancel it. + */ +class StatefulControlSubscriber( + private val controller: ControlsController, + private val provider: ControlsProviderLifecycleManager, + private val bgExecutor: DelayableExecutor +) : IControlsSubscriber.Stub() { + private var subscriptionOpen = false + private var subscription: IControlsSubscription? = null + + companion object { + private const val TAG = "StatefulControlSubscriber" + } + + private fun run(token: IBinder, f: () -> Unit) { + if (provider.token == token) { + bgExecutor.execute { f() } + } + } + + override fun onSubscribe(token: IBinder, subs: IControlsSubscription) { + run(token) { + subscriptionOpen = true + subscription = subs + provider.startSubscription(subs) + } + } + + override fun onNext(token: IBinder, control: Control) { + run(token) { + if (!subscriptionOpen) { + Log.w(TAG, "Refresh outside of window for token:$token") + } else { + controller.refreshStatus(provider.componentName, control) + } + } + } + override fun onError(token: IBinder, error: String) { + run(token) { + if (subscriptionOpen) { + subscriptionOpen = false + Log.e(TAG, "onError receive from '${provider.componentName}': $error") + } + } + } + + override fun onComplete(token: IBinder) { + run(token) { + if (subscriptionOpen) { + subscriptionOpen = false + Log.i(TAG, "onComplete receive from '${provider.componentName}'") + } + } + } + + fun cancel() { + if (!subscriptionOpen) return + bgExecutor.execute { + if (subscriptionOpen) { + subscriptionOpen = false + subscription?.let { + provider.cancelSubscription(it) + } + subscription = null + } + } + } +} 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 04715abe5f99..f2303e622f8d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -19,22 +19,27 @@ package com.android.systemui.controls.management import android.app.Activity import android.content.ComponentName import android.content.Intent +import android.content.res.Configuration import android.graphics.drawable.Drawable import android.os.Bundle import android.text.TextUtils +import android.view.Gravity import android.view.View +import android.view.ViewGroup import android.view.ViewStub import android.widget.Button +import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import androidx.viewpager2.widget.ViewPager2 +import com.android.systemui.Prefs import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.controls.ControlsServiceInfo +import com.android.systemui.controls.TooltipManager import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.qs.PageIndicator import com.android.systemui.settings.CurrentUserTracker import java.text.Collator import java.util.concurrent.Executor @@ -51,6 +56,8 @@ class ControlsFavoritingActivity @Inject constructor( companion object { private const val TAG = "ControlsFavoritingActivity" const val EXTRA_APP = "extra_app_label" + private const val TOOLTIP_PREFS_KEY = Prefs.Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT + private const val TOOLTIP_MAX_SHOWN = 2 } private var component: ComponentName? = null @@ -61,7 +68,9 @@ class ControlsFavoritingActivity @Inject constructor( private lateinit var titleView: TextView private lateinit var iconView: ImageView private lateinit var iconFrame: View - private lateinit var pageIndicator: PageIndicator + private lateinit var pageIndicator: ManagementPageIndicator + private var mTooltipManager: TooltipManager? = null + private lateinit var doneButton: View private var listOfStructures = emptyList<StructureContainer>() private lateinit var comparator: Comparator<StructureContainer> @@ -129,6 +138,7 @@ class ControlsFavoritingActivity @Inject constructor( StructureContainer(it.key, AllModel(it.value, favoriteKeys, emptyZoneString)) }.sortedWith(comparator) executor.execute { + doneButton.isEnabled = true structurePager.adapter = StructureAdapter(listOfStructures) if (error) { statusText.text = resources.getText(R.string.controls_favorite_load_error) @@ -174,7 +184,47 @@ class ControlsFavoritingActivity @Inject constructor( } statusText = requireViewById(R.id.status_message) - pageIndicator = requireViewById(R.id.structure_page_indicator) + if (shouldShowTooltip()) { + mTooltipManager = TooltipManager(statusText.context, + TOOLTIP_PREFS_KEY, TOOLTIP_MAX_SHOWN) + addContentView( + mTooltipManager?.layout, + FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + Gravity.TOP or Gravity.LEFT + ) + ) + } + pageIndicator = requireViewById<ManagementPageIndicator>( + R.id.structure_page_indicator).apply { + addOnLayoutChangeListener(object : View.OnLayoutChangeListener { + override fun onLayoutChange( + v: View, + left: Int, + top: Int, + right: Int, + bottom: Int, + oldLeft: Int, + oldTop: Int, + oldRight: Int, + oldBottom: Int + ) { + if (v.visibility == View.VISIBLE && mTooltipManager != null) { + val p = IntArray(2) + v.getLocationOnScreen(p) + val x = p[0] + (right - left) / 2 + val y = p[1] + bottom - top + mTooltipManager?.show(R.string.controls_structure_tooltip, x, y) + } + } + }) + visibilityListener = { + if (it != View.VISIBLE) { + mTooltipManager?.hide(true) + } + } + } titleView = requireViewById<TextView>(R.id.title).apply { text = appName ?: resources.getText(R.string.controls_favorite_default_title) @@ -184,6 +234,12 @@ class ControlsFavoritingActivity @Inject constructor( iconView = requireViewById(com.android.internal.R.id.icon) iconFrame = requireViewById(R.id.icon_frame) structurePager = requireViewById<ViewPager2>(R.id.structure_pager) + structurePager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + mTooltipManager?.hide(true) + } + }) bindButtons() } @@ -195,23 +251,41 @@ class ControlsFavoritingActivity @Inject constructor( } } - requireViewById<Button>(R.id.done).setOnClickListener { - if (component == null) return@setOnClickListener - listOfStructures.forEach { - val favoritesForStorage = it.model.favorites.map { it.build() } - controller.replaceFavoritesForStructure(StructureInfo(component!!, it.structureName, - favoritesForStorage)) + doneButton = requireViewById<Button>(R.id.done).apply { + isEnabled = false + setOnClickListener { + if (component == null) return@setOnClickListener + listOfStructures.forEach { + val favoritesForStorage = it.model.favorites.map { it.build() } + controller.replaceFavoritesForStructure( + StructureInfo(component!!, it.structureName, favoritesForStorage) + ) + } + finishAffinity() } - - finishAffinity() } } + override fun onPause() { + super.onPause() + mTooltipManager?.hide(false) + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + mTooltipManager?.hide(false) + } + override fun onDestroy() { currentUserTracker.stopTracking() listingController.removeCallback(listingCallback) + controller.cancelLoad() super.onDestroy() } + + private fun shouldShowTooltip(): Boolean { + return Prefs.getInt(applicationContext, TOOLTIP_PREFS_KEY, 0) < TOOLTIP_MAX_SHOWN + } } data class StructureContainer(val structureName: CharSequence, val model: ControlsModel) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt index 4289274cb3e4..72b10982e237 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt @@ -40,4 +40,13 @@ class ManagementPageIndicator( super.setLocation(location) } } + + var visibilityListener: (Int) -> Unit = {} + + override fun onVisibilityChanged(changedView: View, visibility: Int) { + super.onVisibilityChanged(changedView, visibility) + if (changedView == this) { + visibilityListener(visibility) + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index fc5663fe8c97..b4392067d953 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -17,8 +17,8 @@ package com.android.systemui.controls.ui import android.content.Context -import android.graphics.BlendMode import android.graphics.drawable.ClipDrawable +import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.service.controls.Control import android.service.controls.actions.ControlAction @@ -39,6 +39,8 @@ import com.android.systemui.R import kotlin.reflect.KClass private const val UPDATE_DELAY_IN_MILLIS = 3000L +private const val ALPHA_ENABLED = (255.0 * 0.2).toInt() +private const val ALPHA_DISABLED = 255 class ControlViewHolder( val layout: ViewGroup, @@ -69,7 +71,7 @@ class ControlViewHolder( cancelUpdate?.run() - val (status, template) = cws.control?.let { + val (controlStatus, template) = cws.control?.let { title.setText(it.getTitle()) subtitle.setText(it.getSubtitle()) Pair(it.getStatus(), it.getControlTemplate()) @@ -80,20 +82,28 @@ class ControlViewHolder( } cws.control?.let { + layout.setClickable(true) layout.setOnLongClickListener(View.OnLongClickListener() { ControlActionCoordinator.longPress(this@ControlViewHolder) true }) } - val clazz = findBehavior(status, template) + val clazz = findBehavior(controlStatus, template) if (behavior == null || behavior!!::class != clazz) { // Behavior changes can signal a change in template from the app or // first time setup behavior = clazz.java.newInstance() behavior?.initialize(this) + + // let behaviors define their own, if necessary, and clear any existing ones + layout.setAccessibilityDelegate(null) } + behavior?.bind(cws) + + layout.setContentDescription( + "${title.text} ${subtitle.text} ${status.text} ${statusExtra.text}") } fun actionResponse(@ControlAction.ResponseResult response: Int) { @@ -136,16 +146,21 @@ class ControlViewHolder( val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset) val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme()) - val bg = context.getResources().getColorStateList(ri.background, context.getTheme()) + val (bg, alpha) = if (enabled) { + Pair(ri.enabledBackground, ALPHA_ENABLED) + } else { + Pair(R.color.control_default_background, ALPHA_DISABLED) + } + status.setTextColor(fg) statusExtra.setTextColor(fg) icon.setImageDrawable(ri.icon) icon.setImageTintList(fg) - clipLayer.getDrawable().apply { - setTintBlendMode(BlendMode.HUE) - setTintList(bg) + (clipLayer.getDrawable() as GradientDrawable).apply { + setColor(context.getResources().getColor(bg, context.getTheme())) + setAlpha(alpha) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index bde966ca067e..138cd47caae7 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -406,7 +406,7 @@ private class ItemAdapter( setText(item.getTitle()) } view.requireViewById<ImageView>(R.id.app_icon).apply { - setContentDescription(item.getTitle()) + setContentDescription(item.appName) setImageDrawable(item.icon) } return view diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt index 56267beb1b71..27e46497b20a 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt @@ -16,6 +16,7 @@ package com.android.systemui.controls.ui +import android.annotation.ColorRes import android.annotation.MainThread import android.content.ComponentName import android.content.Context @@ -37,8 +38,11 @@ data class IconState(val disabledResourceId: Int, val enabledResourceId: Int) { } } -data class RenderInfo(val icon: Drawable, val foreground: Int, val background: Int) { - +data class RenderInfo( + val icon: Drawable, + val foreground: Int, + @ColorRes val enabledBackground: Int +) { companion object { const val APP_ICON_ID = -1 private val iconMap = SparseArray<Drawable>() @@ -72,6 +76,7 @@ data class RenderInfo(val icon: Drawable, val foreground: Int, val background: I icon = iconMap.get(resourceId) if (icon == null) { icon = context.resources.getDrawable(resourceId, null) + icon.mutate() iconMap.put(resourceId, icon) } } @@ -94,12 +99,13 @@ private const val THERMOSTAT_RANGE = DeviceTypes.TYPE_THERMOSTAT * BUCKET_SIZE private val deviceColorMap = mapOf<Int, Pair<Int, Int>>( (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to - Pair(R.color.thermo_heat_foreground, R.color.thermo_heat_background), + Pair(R.color.thermo_heat_foreground, R.color.control_enabled_thermo_heat_background), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to - Pair(R.color.thermo_cool_foreground, R.color.thermo_cool_background), - DeviceTypes.TYPE_LIGHT to Pair(R.color.light_foreground, R.color.light_background) + Pair(R.color.thermo_cool_foreground, R.color.control_enabled_thermo_cool_background), + DeviceTypes.TYPE_LIGHT + to Pair(R.color.light_foreground, R.color.control_enabled_light_background) ).withDefault { - Pair(R.color.control_foreground, R.color.control_background) + Pair(R.color.control_foreground, R.color.control_enabled_default_background) } private val deviceIconMap = mapOf<Int, IconState>( diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt index 6595b55a691d..c495c58fff2a 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt @@ -16,6 +16,7 @@ package com.android.systemui.controls.ui +import android.os.Bundle import android.content.Context import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable @@ -24,6 +25,9 @@ import android.view.GestureDetector import android.view.GestureDetector.SimpleOnGestureListener import android.view.MotionEvent import android.view.View +import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityNodeInfo import android.widget.TextView import android.service.controls.Control import android.service.controls.actions.FloatAction @@ -94,6 +98,71 @@ class ToggleRangeBehavior : Behavior { updateRange(currentRatio, checked) cvh.applyRenderInfo(checked) + + /* + * This is custom widget behavior, so add a new accessibility delegate to + * handle clicks and range events. Present as a seek bar control. + */ + cvh.layout.setAccessibilityDelegate(object : View.AccessibilityDelegate() { + override fun onInitializeAccessibilityNodeInfo( + host: View, + info: AccessibilityNodeInfo + ) { + super.onInitializeAccessibilityNodeInfo(host, info) + + val min = levelToRangeValue(MIN_LEVEL) + val current = levelToRangeValue(clipLayer.getLevel()) + val max = levelToRangeValue(MAX_LEVEL) + + val step = rangeTemplate.getStepValue().toDouble() + val type = if (step == Math.floor(step)) { + AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT + } else { + AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_FLOAT + } + + val rangeInfo = AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current) + info.setRangeInfo(rangeInfo) + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS) + } + + override fun performAccessibilityAction( + host: View, + action: Int, + arguments: Bundle? + ): Boolean { + val handled = when (action) { + AccessibilityNodeInfo.ACTION_CLICK -> { + ControlActionCoordinator.toggle(cvh, template.getTemplateId(), + template.isChecked()) + true + } + AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS.getId() -> { + if (arguments == null || !arguments.containsKey( + AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)) { + false + } else { + val value = arguments.getFloat( + AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE) + val ratioDiff = (value - rangeTemplate.getCurrentValue()) / + (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()) + updateRange(ratioDiff, template.isChecked()) + endUpdateRange() + true + } + } + else -> false + } + + return handled || super.performAccessibilityAction(host, action, arguments) + } + + override fun onRequestSendAccessibilityEvent( + host: ViewGroup, + child: View, + event: AccessibilityEvent + ): Boolean = false + }) } fun beginUpdateRange() { @@ -108,7 +177,7 @@ class ToggleRangeBehavior : Behavior { clipLayer.setLevel(newLevel) if (checked) { - val newValue = levelToRangeValue() + val newValue = levelToRangeValue(clipLayer.getLevel()) val formattedNewValue = format(rangeTemplate.getFormatString().toString(), DEFAULT_FORMAT, newValue) @@ -133,8 +202,8 @@ class ToggleRangeBehavior : Behavior { } } - private fun levelToRangeValue(): Float { - val ratio = clipLayer.getLevel().toFloat() / MAX_LEVEL + private fun levelToRangeValue(i: Int): Float { + val ratio = i.toFloat() / MAX_LEVEL return rangeTemplate.getMinValue() + (ratio * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())) } @@ -143,7 +212,8 @@ class ToggleRangeBehavior : Behavior { statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources() .getDimensionPixelSize(R.dimen.control_status_normal).toFloat()) status.setVisibility(View.VISIBLE) - cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(levelToRangeValue()))) + cvh.action(FloatAction(rangeTemplate.getTemplateId(), + findNearestStep(levelToRangeValue(clipLayer.getLevel())))) } fun findNearestStep(value: Float): Float { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 6c502d273a1c..3a4b273e1c98 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -90,7 +90,7 @@ public class DependencyProvider { /** */ @Provides - public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) { + public AmbientDisplayConfiguration provideAmbientDisplayConfiguration(Context context) { return new AmbientDisplayConfiguration(context); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 513580f55a87..2e9ce1200c85 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -19,7 +19,6 @@ package com.android.systemui.dagger; import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; -import android.view.Choreographer; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.BootCompleteCache; @@ -31,22 +30,16 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.Recents; import com.android.systemui.stackdivider.Divider; -import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.NotificationShadeWindowBlurController; -import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.people.PeopleHubModule; import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent; -import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardLiftController; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.ConcurrencyModule; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.time.SystemClock; @@ -98,21 +91,6 @@ public abstract class SystemUIModule { keyguardUpdateMonitor, dumpManager); } - @Singleton - @Provides - @Nullable - static NotificationShadeWindowBlurController providesBlurController(BlurUtils blurUtils, - SysuiStatusBarStateController statusBarStateController, - DumpManager dumpManager, BiometricUnlockController biometricUnlockController, - KeyguardStateController keyguardStateController, - NotificationShadeWindowController notificationShadeWindowController, - Choreographer choreographer) { - return blurUtils.supportsBlursOnWindows() ? new NotificationShadeWindowBlurController( - statusBarStateController, blurUtils, biometricUnlockController, - keyguardStateController, notificationShadeWindowController, choreographer, - dumpManager) : null; - } - /** */ @Binds public abstract NotificationRowBinder bindNotificationRowBinder( diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index c45063a52f78..f6fccc00bf99 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -161,15 +161,8 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi } int scrimOpacity = -1; - if (mPaused || mScreenOff) { - // If AOD is paused, force the screen black until the - // sensor reports a new brightness. This ensures that when the screen comes on - // again, it will only show after the brightness sensor has stabilized, - // avoiding a potential flicker. - scrimOpacity = 255; - } else if (!mScreenOff && mLightSensor == null) { - // No light sensor but previous state turned the screen black. Make the scrim - // transparent and below views visible. + if (mLightSensor == null) { + // No light sensor, scrims are always transparent. scrimOpacity = 0; } else if (brightnessReady) { // Only unblank scrim once brightness is ready. diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 786ad2c7d82a..59b28b4372b4 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -99,7 +99,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; -import com.android.systemui.statusbar.BlurUtils; +import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -162,7 +162,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final IActivityManager mIActivityManager; private final TelecomManager mTelecomManager; private final MetricsLogger mMetricsLogger; - private final BlurUtils mBlurUtils; + private final NotificationShadeDepthController mDepthController; private ArrayList<Action> mItems; private ActionsDialog mDialog; @@ -207,7 +207,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, KeyguardStateController keyguardStateController, UserManager userManager, TrustManager trustManager, IActivityManager iActivityManager, @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, - BlurUtils blurUtils, SysuiColorExtractor colorExtractor, + NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, ControlsUiController controlsUiController, IWindowManager iWindowManager, @@ -229,7 +229,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mIActivityManager = iActivityManager; mTelecomManager = telecomManager; mMetricsLogger = metricsLogger; - mBlurUtils = blurUtils; + mDepthController = depthController; mSysuiColorExtractor = colorExtractor; mStatusBarService = statusBarService; mNotificationShadeWindowController = notificationShadeWindowController; @@ -437,7 +437,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, : null; ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController, - mBlurUtils, mSysuiColorExtractor, mStatusBarService, + mDepthController, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, shouldShowControls() ? mControlsUiController : null); dialog.setCanceledOnTouchOutside(false); // Handled by the custom class. @@ -1570,20 +1570,21 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private ResetOrientationData mResetOrientationData; private boolean mHadTopUi; private final NotificationShadeWindowController mNotificationShadeWindowController; - private final BlurUtils mBlurUtils; + private final NotificationShadeDepthController mDepthController; private ControlsUiController mControlsUiController; private ViewGroup mControlsView; ActionsDialog(Context context, MyAdapter adapter, - GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils, + GlobalActionsPanelPlugin.PanelViewController plugin, + NotificationShadeDepthController depthController, SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, ControlsUiController controlsUiController) { super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions); mContext = context; mAdapter = adapter; - mBlurUtils = blurUtils; + mDepthController = depthController; mColorExtractor = sysuiColorExtractor; mStatusBarService = statusBarService; mNotificationShadeWindowController = notificationShadeWindowController; @@ -1792,8 +1793,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, float animatedValue = animation.getAnimatedFraction(); int alpha = (int) (animatedValue * mScrimAlpha * 255); mBackgroundDrawable.setAlpha(alpha); - mBlurUtils.applyBlur(mGlobalActionsLayout.getViewRootImpl(), - mBlurUtils.radiusForRatio(animatedValue)); + mDepthController.updateGlobalDialogVisibility(animatedValue, + mGlobalActionsLayout); }) .start(); if (mControlsUiController != null) { @@ -1822,8 +1823,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, float animatedValue = 1f - animation.getAnimatedFraction(); int alpha = (int) (animatedValue * mScrimAlpha * 255); mBackgroundDrawable.setAlpha(alpha); - mBlurUtils.applyBlur(mGlobalActionsLayout.getViewRootImpl(), - mBlurUtils.radiusForRatio(animatedValue)); + mDepthController.updateGlobalDialogVisibility(animatedValue, + mGlobalActionsLayout); }) .start(); dismissPanel(); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 280a24870ef3..bbb57bdb33f7 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -151,7 +151,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks if (mBlurUtils.supportsBlursOnWindows()) { background.setAlpha((int) (ScrimController.BUSY_SCRIM_ALPHA * 255)); mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(), - mBlurUtils.radiusForRatio(1)); + mBlurUtils.blurRadiusOfRatio(1)); } else { background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255)); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java index ae380b72f5e0..c281ece59c5a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java @@ -93,7 +93,7 @@ public class WorkLockActivityController { return mIatm.startActivityAsUser( mContext.getIApplicationThread() /*caller*/, mContext.getBasePackageName() /*callingPackage*/, - mContext.getFeatureId() /*callingFeatureId*/, + mContext.getAttributionTag() /*callingAttributionTag*/, intent /*intent*/, intent.resolveTypeIfNeeded(mContext.getContentResolver()) /*resolvedType*/, null /*resultTo*/, diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java index 67802bc9888d..41bace74bd46 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -37,7 +37,6 @@ public class PipAnimationController { private static final float FRACTION_START = 0f; private static final float FRACTION_END = 1f; - public static final int DURATION_DEFAULT_MS = 425; public static final int ANIM_TYPE_BOUNDS = 0; public static final int ANIM_TYPE_ALPHA = 1; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 00b977e1d558..90b657077b0d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -18,7 +18,6 @@ package com.android.systemui.pip; import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS; -import static com.android.systemui.pip.PipAnimationController.DURATION_DEFAULT_MS; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_SAME; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN; @@ -81,6 +80,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { private final Rect mLastReportedBounds = new Rect(); private final int mCornerRadius; private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>(); + private final int mEnterExitAnimationDuration; // These callbacks are called on the update thread private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = @@ -180,6 +180,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { mPipBoundsHandler = boundsHandler; mPipAnimationController = new PipAnimationController(context); mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius); + mEnterExitAnimationDuration = context.getResources() + .getInteger(R.integer.config_pipResizeAnimationDuration); } public Handler getUpdateHandler() { @@ -235,14 +237,14 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { mBoundsToRestore.put(mToken.asBinder(), currentBounds); if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { scheduleAnimateResizePip(currentBounds, destinationBounds, - TRANSITION_DIRECTION_TO_PIP, DURATION_DEFAULT_MS, null); + TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { mUpdateHandler.post(() -> mPipAnimationController .getAnimator(mLeash, destinationBounds, 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setCornerRadius(mCornerRadius) .setPipAnimationCallback(mPipAnimationCallback) - .setDuration(DURATION_DEFAULT_MS) + .setDuration(mEnterExitAnimationDuration) .start()); mOneShotAnimationType = ANIM_TYPE_BOUNDS; } else { @@ -260,7 +262,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } final Rect boundsToRestore = mBoundsToRestore.remove(mToken.asBinder()); scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore, - TRANSITION_DIRECTION_TO_FULLSCREEN, DURATION_DEFAULT_MS, null); + TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration, null); mInPip = false; } @@ -278,7 +280,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( getAspectRatioOrDefault(newParams), null /* bounds */); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); - scheduleAnimateResizePip(destinationBounds, DURATION_DEFAULT_MS, null); + scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration, null); } /** @@ -415,11 +417,16 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } mLastReportedBounds.set(destinationBounds); try { + // If we are animating to fullscreen, then we need to reset the override bounds on the + // task to ensure that the task "matches" the parent's bounds + Rect taskBounds = direction == TRANSITION_DIRECTION_TO_FULLSCREEN + ? null + : destinationBounds; final WindowContainerTransaction wct = new WindowContainerTransaction(); if (direction == TRANSITION_DIRECTION_TO_PIP) { - wct.scheduleFinishEnterPip(mToken, destinationBounds); + wct.scheduleFinishEnterPip(mToken, taskBounds); } else { - wct.setBounds(mToken, destinationBounds); + wct.setBounds(mToken, taskBounds); } wct.setBoundsChangeTransaction(mToken, tx); mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java index b09d6e163b77..7dfd99c2110d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java @@ -44,7 +44,7 @@ public class PipAppOpsListener { try { // Dismiss the PiP once the user disables the app ops setting for that package final Pair<ComponentName, Integer> topPipActivityInfo = - PipUtils.getTopPinnedActivity(mContext, mActivityManager); + PipUtils.getTopPipActivity(mContext, mActivityManager); if (topPipActivityInfo.first != null) { final ApplicationInfo appInfo = mContext.getPackageManager() .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index eed81bfc07f9..2aac188c130d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -115,7 +115,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Override public void onActivityUnpinned() { - final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPinnedActivity( + final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPipActivity( mContext, mActivityManager); final ComponentName topActivity = topPipActivityInfo.first; mMenuController.onActivityUnpinned(); @@ -128,7 +128,12 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio } @Override - public void onPinnedActivityRestartAttempt(boolean clearedTask) { + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask) { + if (task.configuration.windowConfiguration.getWindowingMode() + != WINDOWING_MODE_PINNED) { + return; + } mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */); } }; @@ -232,6 +237,12 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mTouchHandler.getMotionHelper()); displayController.addDisplayChangingController(mRotationController); + // Ensure that we have the display info in case we get calls to update the bounds before the + // listener calls back + final DisplayInfo displayInfo = new DisplayInfo(); + context.getDisplay().getDisplayInfo(displayInfo); + mPipBoundsHandler.onDisplayInfoChanged(displayInfo); + try { ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer( mPipTaskOrganizer, WINDOWING_MODE_PINNED); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java index e57b4166937f..849a62add80f 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java @@ -231,7 +231,7 @@ public class PipMediaController { */ private void resolveActiveMediaController(List<MediaController> controllers) { if (controllers != null) { - final ComponentName topActivity = PipUtils.getTopPinnedActivity(mContext, + final ComponentName topActivity = PipUtils.getTopPipActivity(mContext, mActivityManager).first; if (topActivity != null) { for (int i = 0; i < controllers.size(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index fc04f795c056..2b9b1716cb18 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -569,7 +569,7 @@ public class PipMenuActivity extends Activity { private void showSettings() { final Pair<ComponentName, Integer> topPipActivityInfo = - PipUtils.getTopPinnedActivity(this, ActivityManager.getService()); + PipUtils.getTopPipActivity(this, ActivityManager.getService()); if (topPipActivityInfo.first != null) { final UserHandle user = UserHandle.of(topPipActivityInfo.second); final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 90db91a7a29e..b5fb1a9dba9a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -31,6 +31,7 @@ import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.util.Log; +import android.util.Pair; import android.util.Size; import android.view.IPinnedStackController; import android.view.InputEvent; @@ -148,8 +149,11 @@ public class PipTouchHandler { @Override public void onPipDismiss() { - MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext, - PipUtils.getTopPinnedActivity(mContext, mActivityManager)); + Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext, + mActivityManager); + if (topPipActivity.first != null) { + MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext, topPipActivity); + } mMotionHelper.dismissPip(); } @@ -653,7 +657,7 @@ public class PipTouchHandler { // Check if the user dragged or flung the PiP offscreen to dismiss it if (mMotionHelper.shouldDismissPip() || isFlingToBot) { MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, - PipUtils.getTopPinnedActivity(mContext, mActivityManager)); + PipUtils.getTopPipActivity(mContext, mActivityManager)); mMotionHelper.animateDismiss( vel.x, vel.y, PipTouchHandler.this::updateDismissFraction /* updateAction */); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java index 1ed1904d30fb..4cfec0193b54 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java @@ -36,7 +36,7 @@ public class PipUtils { * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack. * The component name may be null if no such activity exists. */ - public static Pair<ComponentName, Integer> getTopPinnedActivity(Context context, + public static Pair<ComponentName, Integer> getTopPipActivity(Context context, IActivityManager activityManager) { try { final String sysUiPackageName = context.getPackageName(); diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index a5e9dbc41924..6206dd576ec4 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -20,8 +20,6 @@ import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static com.android.systemui.pip.PipAnimationController.DURATION_DEFAULT_MS; - import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.StackInfo; import android.app.ActivityTaskManager; @@ -136,6 +134,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private String[] mLastPackagesResourceGranted; private PipNotification mPipNotification; private ParceledListSlice mCustomActions; + private int mResizeAnimationDuration; // Used to calculate the movement bounds private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); @@ -238,6 +237,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mInitialized = true; mContext = context; mPipBoundsHandler = pipBoundsHandler; + mResizeAnimationDuration = context.getResources() + .getInteger(R.integer.config_pipResizeAnimationDuration); mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler); mPipTaskOrganizer.registerPipTransitionCallback(this); mActivityTaskManager = ActivityTaskManager.getService(); @@ -436,7 +437,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mCurrentPipBounds = mPipBounds; break; } - mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, DURATION_DEFAULT_MS, null); + mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, mResizeAnimationDuration, + null); } /** @@ -680,7 +682,12 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio } @Override - public void onPinnedActivityRestartAttempt(boolean clearedTask) { + public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) { + if (task.configuration.windowConfiguration.getWindowingMode() + != WINDOWING_MODE_PINNED) { + return; + } if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()"); // If PIPed activity is launched again by Launcher or intent, make it fullscreen. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 17ac5e5ca47e..fab71918a3d1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -274,8 +274,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D try { tile = createTile(tileSpec); if (tile != null) { + tile.setTileSpec(tileSpec); if (tile.isAvailable()) { - tile.setTileSpec(tileSpec); newTiles.put(tileSpec, tile); mQSLogger.logTileAdded(tileSpec); } else { diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java index f3e2f104621e..5bf44c6a3003 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java @@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; + import android.annotation.Nullable; import android.app.ActivityManager; import android.app.trust.TrustManager; @@ -37,6 +39,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.phone.StatusBar; @@ -63,6 +66,21 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation { private TrustManager mTrustManager; private OverviewProxyService mOverviewProxyService; + private TaskStackChangeListener mListener = new TaskStackChangeListener() { + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask) { + if (task.configuration.windowConfiguration.getWindowingMode() + != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + return; + } + + if (homeTaskVisible) { + showRecentApps(false /* triggeredFromAltTab */); + } + } + }; + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Inject public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy, @@ -77,6 +95,7 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation { mHandler = new Handler(); mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE); mOverviewProxyService = Dependency.get(OverviewProxyService.class); + ActivityManagerWrapper.getInstance().registerTaskStackListener(mListener); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index fb6815336548..ea5ec0503067 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -148,6 +148,8 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, * regardless of what has focus. */ private boolean mTargetShown = false; + private float mTargetPrimaryDim = 0.f; + private float mTargetSecondaryDim = 0.f; // The following are the current (most recent) states set during animation /** {@code true} if the secondary split has IME focus. */ @@ -186,8 +188,12 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, @Override public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop, boolean imeShouldShow, SurfaceControl.Transaction t) { + if (!inSplitMode()) { + return; + } + final boolean splitIsVisible = !mView.isHidden(); mSecondaryHasFocus = getSecondaryHasFocus(displayId); - mTargetAdjusted = imeShouldShow && mSecondaryHasFocus + mTargetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus && !mSplitLayout.mDisplayLayout.isLandscape(); mHiddenTop = hiddenTop; mShownTop = shownTop; @@ -195,6 +201,10 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, if (mLastAdjustTop < 0) { mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop; } + mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible) + ? ADJUSTED_NONFOCUS_DIM : 0.f; + mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible) + ? ADJUSTED_NONFOCUS_DIM : 0.f; if (mAnimation != null || (mImeWasShown && imeShouldShow && mTargetAdjusted != mAdjusted)) { // We need to animate adjustment independently of the IME position, so @@ -202,7 +212,11 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, // different split's editor has gained focus while the IME is still visible. startAsyncAnimation(); } - updateImeAdjustState(); + if (splitIsVisible) { + // If split is hidden, we don't want to trigger any relayouts that would cause the + // divider to show again. + updateImeAdjustState(); + } } private void updateImeAdjustState() { @@ -245,7 +259,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, @Override public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) { - if (mAnimation != null) { + if (mAnimation != null || !inSplitMode()) { // Not synchronized with IME anymore, so return. return; } @@ -257,7 +271,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, @Override public void onImeEndPositioning(int displayId, boolean cancelled, SurfaceControl.Transaction t) { - if (mAnimation != null) { + if (mAnimation != null || !inSplitMode()) { // Not synchronized with IME anymore, so return. return; } @@ -273,14 +287,10 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, mSplitLayout.mAdjustedSecondary); } final float invProg = 1.f - progress; - final float targetPrimaryDim = - (mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f; - final float targetSecondaryDim = - (!mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f; mView.setResizeDimLayer(t, true /* primary */, - mLastPrimaryDim * invProg + progress * targetPrimaryDim); + mLastPrimaryDim * invProg + progress * mTargetPrimaryDim); mView.setResizeDimLayer(t, false /* primary */, - mLastSecondaryDim * invProg + progress * targetSecondaryDim); + mLastSecondaryDim * invProg + progress * mTargetSecondaryDim); } private void onEnd(boolean cancelled, SurfaceControl.Transaction t) { @@ -289,10 +299,8 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, mAdjusted = mTargetAdjusted; mImeWasShown = mTargetShown; mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop; - mLastPrimaryDim = - (mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f; - mLastSecondaryDim = - (!mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f; + mLastPrimaryDim = mTargetPrimaryDim; + mLastSecondaryDim = mTargetSecondaryDim; } } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 477cbb7c7ad0..4114bb9d055d 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -165,6 +165,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, // The view is removed or in the process of been removed from the system. private boolean mRemoved; + // Whether the surface for this view has been hidden regardless of actual visibility. This is + // used interact with keyguard. + private boolean mSurfaceHidden = false; + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -414,6 +418,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, /** Unlike setVisible, this directly hides the surface without changing view visibility. */ void setHidden(boolean hidden) { + if (mSurfaceHidden == hidden) { + return; + } + mSurfaceHidden = hidden; post(() -> { final SurfaceControl sc = getWindowSurfaceControl(); if (sc == null) { @@ -430,6 +438,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, }); } + boolean isHidden() { + return mSurfaceHidden; + } + public boolean startDragging(boolean animate, boolean touching) { cancelFlingAnimation(); if (touching) { @@ -1071,7 +1083,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, void setResizeDimLayer(Transaction t, boolean primary, float alpha) { SurfaceControl dim = primary ? mTiles.mPrimaryDim : mTiles.mSecondaryDim; - if (alpha <= 0.f) { + if (alpha <= 0.001f) { t.hide(dim); } else { t.setAlpha(dim, alpha); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index 34c0860c21a2..c523b7b7655e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -50,7 +50,7 @@ open class BlurUtils @Inject constructor( /** * Translates a ratio from 0 to 1 to a blur radius in pixels. */ - fun radiusForRatio(ratio: Float): Int { + fun blurRadiusOfRatio(ratio: Float): Int { if (ratio == 0f) { return 0 } @@ -58,6 +58,17 @@ open class BlurUtils @Inject constructor( } /** + * Translates a blur radius in pixels to a ratio between 0 to 1. + */ + fun ratioOfBlurRadius(blur: Int): Float { + if (blur == 0) { + return 0f + } + return MathUtils.map(minBlurRadius.toFloat(), maxBlurRadius.toFloat(), + 0f /* maxStart */, 1f /* maxStop */, blur.toFloat()) + } + + /** * Applies background blurs to a {@link ViewRootImpl}. * * @param viewRootImpl The window root. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java index 0bfcdbdb1118..4759d56099f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java @@ -23,6 +23,7 @@ import android.graphics.drawable.Icon; import android.text.TextUtils; import android.view.NotificationHeaderView; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; @@ -182,24 +183,24 @@ public class NotificationHeaderUtil { private void sanitizeChild(View child) { if (child != null) { - NotificationHeaderView header = (NotificationHeaderView) child.findViewById( + ViewGroup header = child.findViewById( com.android.internal.R.id.notification_header); sanitizeHeader(header); } } - private void sanitizeHeader(NotificationHeaderView rowHeader) { + private void sanitizeHeader(ViewGroup rowHeader) { if (rowHeader == null) { return; } final int childCount = rowHeader.getChildCount(); View time = rowHeader.findViewById(com.android.internal.R.id.time); boolean hasVisibleText = false; - for (int i = 1; i < childCount - 1 ; i++) { + for (int i = 0; i < childCount; i++) { View child = rowHeader.getChildAt(i); if (child instanceof TextView && child.getVisibility() != View.GONE - && !mDividers.contains(Integer.valueOf(child.getId())) + && !mDividers.contains(child.getId()) && child != time) { hasVisibleText = true; break; @@ -212,14 +213,14 @@ public class NotificationHeaderUtil { time.setVisibility(timeVisibility); View left = null; View right; - for (int i = 1; i < childCount - 1 ; i++) { + for (int i = 0; i < childCount; i++) { View child = rowHeader.getChildAt(i); - if (mDividers.contains(Integer.valueOf(child.getId()))) { + if (mDividers.contains(child.getId())) { boolean visible = false; // Lets find the item to the right - for (i++; i < childCount - 1; i++) { + for (i++; i < childCount; i++) { right = rowHeader.getChildAt(i); - if (mDividers.contains(Integer.valueOf(right.getId()))) { + if (mDividers.contains(right.getId())) { // A divider was found, this needs to be hidden i--; break; @@ -276,14 +277,18 @@ public class NotificationHeaderUtil { if (!mApply) { return; } - NotificationHeaderView header = row.getContractedNotificationHeader(); - if (header == null) { - // No header found. We still consider this to be the same to avoid weird flickering + View contractedChild = row.getPrivateLayout().getContractedChild(); + if (contractedChild == null) { + return; + } + View ownView = contractedChild.findViewById(mId); + if (ownView == null) { + // No view found. We still consider this to be the same to avoid weird flickering // when for example showing an undo notification return; } Object childData = mExtractor == null ? null : mExtractor.extractData(row); - mApply = mComparator.compare(mParentView, header.findViewById(mId), + mApply = mComparator.compare(mParentView, ownView, mParentData, childData); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 6e905a30e590..eb8526d0ef91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator +import android.app.WallpaperManager import android.view.Choreographer import android.view.View import androidx.dynamicanimation.animation.FloatPropertyCompat @@ -30,7 +31,6 @@ import com.android.systemui.Interpolators import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK -import com.android.systemui.statusbar.phone.NotificationShadeWindowController import com.android.systemui.statusbar.phone.PanelExpansionListener import com.android.systemui.statusbar.policy.KeyguardStateController import java.io.FileDescriptor @@ -43,13 +43,13 @@ import kotlin.math.max * Controller responsible for statusbar window blur. */ @Singleton -class NotificationShadeWindowBlurController @Inject constructor( +class NotificationShadeDepthController @Inject constructor( private val statusBarStateController: SysuiStatusBarStateController, private val blurUtils: BlurUtils, private val biometricUnlockController: BiometricUnlockController, private val keyguardStateController: KeyguardStateController, - private val notificationShadeWindowController: NotificationShadeWindowController, private val choreographer: Choreographer, + private val wallpaperManager: WallpaperManager, dumpManager: DumpManager ) : PanelExpansionListener, Dumpable { companion object { @@ -58,20 +58,22 @@ class NotificationShadeWindowBlurController @Inject constructor( } lateinit var root: View + private var blurRoot: View? = null private var keyguardAnimator: Animator? = null private var notificationAnimator: Animator? = null private var updateScheduled: Boolean = false private var shadeExpansion = 1.0f private val shadeSpring = SpringAnimation(this, object : - FloatPropertyCompat<NotificationShadeWindowBlurController>("shadeBlurRadius") { - override fun setValue(rect: NotificationShadeWindowBlurController?, value: Float) { + FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") { + override fun setValue(rect: NotificationShadeDepthController?, value: Float) { shadeBlurRadius = value.toInt() } - override fun getValue(rect: NotificationShadeWindowBlurController?): Float { + override fun getValue(rect: NotificationShadeDepthController?): Float { return shadeBlurRadius.toFloat() } }) + private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE private var shadeBlurRadius = 0 set(value) { if (field == value) return @@ -84,12 +86,7 @@ class NotificationShadeWindowBlurController @Inject constructor( field = value scheduleUpdate() } - private var incomingNotificationBlurRadius = 0 - set(value) { - if (field == value) return - field = value - scheduleUpdate() - } + private var globalDialogVisibility = 0f /** * Callback that updates the window blur value and is called only once per frame. @@ -97,13 +94,12 @@ class NotificationShadeWindowBlurController @Inject constructor( private val updateBlurCallback = Choreographer.FrameCallback { updateScheduled = false - var notificationBlur = 0 - if (statusBarStateController.state == StatusBarState.KEYGUARD) { - notificationBlur = (incomingNotificationBlurRadius * shadeExpansion).toInt() - } - - val blur = max(max(shadeBlurRadius, wakeAndUnlockBlurRadius), notificationBlur) - blurUtils.applyBlur(root.viewRootImpl, blur) + val blur = max(shadeBlurRadius, + max(wakeAndUnlockBlurRadius, blurUtils.blurRadiusOfRatio(globalDialogVisibility))) + blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur) + val rawZoom = max(blurUtils.ratioOfBlurRadius(blur), globalDialogVisibility) + wallpaperManager.setWallpaperZoomOut(root.windowToken, + zoomInterpolator.getInterpolation(rawZoom)) } /** @@ -123,7 +119,7 @@ class NotificationShadeWindowBlurController @Inject constructor( interpolator = Interpolators.DECELERATE_QUINT addUpdateListener { animation: ValueAnimator -> wakeAndUnlockBlurRadius = - blurUtils.radiusForRatio(animation.animatedValue as Float) + blurUtils.blurRadiusOfRatio(animation.animatedValue as Float) } addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { @@ -163,7 +159,7 @@ class NotificationShadeWindowBlurController @Inject constructor( var newBlur = 0 if (statusBarStateController.state == StatusBarState.SHADE) { - newBlur = blurUtils.radiusForRatio(expansion) + newBlur = blurUtils.blurRadiusOfRatio(expansion) } if (shadeBlurRadius == newBlur) { @@ -172,20 +168,29 @@ class NotificationShadeWindowBlurController @Inject constructor( shadeSpring.animateToFinalPosition(newBlur.toFloat()) } - private fun scheduleUpdate() { + private fun scheduleUpdate(viewToBlur: View? = null) { if (updateScheduled) { return } updateScheduled = true + blurRoot = viewToBlur choreographer.postFrameCallback(updateBlurCallback) } + fun updateGlobalDialogVisibility(visibility: Float, dialogView: View) { + if (visibility == globalDialogVisibility) { + return + } + globalDialogVisibility = visibility + scheduleUpdate(dialogView) + } + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { - IndentingPrintWriter(pw, " ").use { + IndentingPrintWriter(pw, " ").let { it.println("StatusBarWindowBlurController:") it.increaseIndent() it.println("shadeBlurRadius: $shadeBlurRadius") it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius") } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java index 7b5a70eb5430..2a45bc210580 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java @@ -41,6 +41,7 @@ public class ViewTransformationHelper implements TransformableView, private static final int TAG_CONTAINS_TRANSFORMED_VIEW = R.id.contains_transformed_view; private ArrayMap<Integer, View> mTransformedViews = new ArrayMap<>(); + private ArraySet<Integer> mKeysTransformingToSimilar = new ArraySet<>(); private ArrayMap<Integer, CustomTransformation> mCustomTransformations = new ArrayMap<>(); private ValueAnimator mViewTransformationAnimation; @@ -48,8 +49,22 @@ public class ViewTransformationHelper implements TransformableView, mTransformedViews.put(key, transformedView); } + /** + * Add a view that transforms to a similar sibling, meaning that we should consider any mapping + * found treated as the same viewType. This is useful for imageViews, where it's hard to compare + * if the source images are the same when they are bitmap based. + * + * @param key The key how this is added + * @param transformedView the view that is added + */ + public void addViewTransformingToSimilar(int key, View transformedView) { + addTransformedView(key, transformedView); + mKeysTransformingToSimilar.add(key); + } + public void reset() { mTransformedViews.clear(); + mKeysTransformingToSimilar.clear(); } public void setCustomTransformation(CustomTransformation transformation, int viewType) { @@ -60,7 +75,11 @@ public class ViewTransformationHelper implements TransformableView, public TransformState getCurrentState(int fadingView) { View view = mTransformedViews.get(fadingView); if (view != null && view.getVisibility() != View.GONE) { - return TransformState.createFrom(view, this); + TransformState transformState = TransformState.createFrom(view, this); + if (mKeysTransformingToSimilar.contains(fadingView)) { + transformState.setIsSameAsAnyView(true); + } + return transformState; } return null; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java index b732966b32db..9383f537db45 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java @@ -21,9 +21,9 @@ import android.util.Pools; import android.view.View; import android.view.ViewGroup; +import com.android.internal.widget.IMessagingLayout; import com.android.internal.widget.MessagingGroup; import com.android.internal.widget.MessagingImageMessage; -import com.android.internal.widget.MessagingLayout; import com.android.internal.widget.MessagingLinearLayout; import com.android.internal.widget.MessagingMessage; import com.android.internal.widget.MessagingPropertyAnimator; @@ -41,7 +41,7 @@ public class MessagingLayoutTransformState extends TransformState { private static Pools.SimplePool<MessagingLayoutTransformState> sInstancePool = new Pools.SimplePool<>(40); private MessagingLinearLayout mMessageContainer; - private MessagingLayout mMessagingLayout; + private IMessagingLayout mMessagingLayout; private HashMap<MessagingGroup, MessagingGroup> mGroupMap = new HashMap<>(); private float mRelativeTranslationOffset; @@ -266,8 +266,9 @@ public class MessagingLayoutTransformState extends TransformState { transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */ useLinearTransformation); boolean otherIsIsolated = otherGroup.getIsolatedMessage() == otherChild; - if (transformationAmount == 0.0f && otherIsIsolated) { - ownGroup.setTransformingImages(true); + if (transformationAmount == 0.0f + && (otherIsIsolated || otherGroup.isSingleLine())) { + ownGroup.setClippingDisabled(true); } if (otherChild == null) { child.setTranslationY(previousTranslation); @@ -291,11 +292,20 @@ public class MessagingLayoutTransformState extends TransformState { if (useLinearTransformation) { ownState.setDefaultInterpolator(Interpolators.LINEAR); } - ownState.setIsSameAsAnyView(sameAsAny); + ownState.setIsSameAsAnyView(sameAsAny && !isGone(otherView)); if (to) { if (otherView != null) { TransformState otherState = TransformState.createFrom(otherView, mTransformInfo); - ownState.transformViewTo(otherState, transformationAmount); + if (!isGone(otherView)) { + ownState.transformViewTo(otherState, transformationAmount); + } else { + if (!isGone(ownView)) { + ownState.disappear(transformationAmount, null); + } + // We still want to transform vertically if the view is gone, + // since avatars serve as anchors for the rest of the layout transition + ownState.transformViewVerticalTo(otherState, transformationAmount); + } otherState.recycle(); } else { ownState.disappear(transformationAmount, null); @@ -303,7 +313,16 @@ public class MessagingLayoutTransformState extends TransformState { } else { if (otherView != null) { TransformState otherState = TransformState.createFrom(otherView, mTransformInfo); - ownState.transformViewFrom(otherState, transformationAmount); + if (!isGone(otherView)) { + ownState.transformViewFrom(otherState, transformationAmount); + } else { + if (!isGone(ownView)) { + ownState.appear(transformationAmount, null); + } + // We still want to transform vertically if the view is gone, + // since avatars serve as anchors for the rest of the layout transition + ownState.transformViewVerticalFrom(otherState, transformationAmount); + } otherState.recycle(); } else { ownState.appear(transformationAmount, null); @@ -337,6 +356,9 @@ public class MessagingLayoutTransformState extends TransformState { } private boolean isGone(View view) { + if (view == null) { + return true; + } if (view.getVisibility() == View.GONE) { return true; } @@ -408,7 +430,7 @@ public class MessagingLayoutTransformState extends TransformState { ownGroup.getMessageContainer().setTranslationY(0); ownGroup.getSenderView().setTranslationY(0); } - ownGroup.setTransformingImages(false); + ownGroup.setClippingDisabled(false); ownGroup.updateClipRect(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 8a23e3796e9b..27476964b9af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.notification import android.animation.ObjectAnimator -import android.content.Context import android.util.FloatProperty import com.android.systemui.Interpolators import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -26,10 +25,10 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.PanelExpansionListener +import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import javax.inject.Inject @@ -37,15 +36,14 @@ import javax.inject.Singleton @Singleton class NotificationWakeUpCoordinator @Inject constructor( - private val mHeadsUpManagerPhone: HeadsUpManagerPhone, - private val statusBarStateController: StatusBarStateController, - private val bypassController: KeyguardBypassController, - private val dozeParameters: DozeParameters) - : OnHeadsUpChangedListener, StatusBarStateController.StateListener, - PanelExpansionListener { + private val mHeadsUpManager: HeadsUpManager, + private val statusBarStateController: StatusBarStateController, + private val bypassController: KeyguardBypassController, + private val dozeParameters: DozeParameters +) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener { - private val mNotificationVisibility - = object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") { + private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>( + "notificationVisibility") { override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) { coordinator.setVisibilityAmount(value) @@ -78,10 +76,10 @@ class NotificationWakeUpCoordinator @Inject constructor( field = value willWakeUp = false if (value) { - if (mNotificationsVisible && !mNotificationsVisibleForExpansion - && !bypassController.bypassEnabled) { + if (mNotificationsVisible && !mNotificationsVisibleForExpansion && + !bypassController.bypassEnabled) { // We're waking up while pulsing, let's make sure the animation looks nice - mStackScroller.wakeUpFromPulse(); + mStackScroller.wakeUpFromPulse() } if (bypassController.bypassEnabled && !mNotificationsVisible) { // Let's make sure our huns become visible once we are waking up in case @@ -100,7 +98,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } private var collapsedEnoughToHide: Boolean = false - lateinit var iconAreaController : NotificationIconAreaController + lateinit var iconAreaController: NotificationIconAreaController var pulsing: Boolean = false set(value) { @@ -132,8 +130,8 @@ class NotificationWakeUpCoordinator @Inject constructor( var canShow = pulsing if (bypassController.bypassEnabled) { // We also allow pulsing on the lock screen! - canShow = canShow || (wakingUp || willWakeUp || fullyAwake) - && statusBarStateController.state == StatusBarState.KEYGUARD + canShow = canShow || (wakingUp || willWakeUp || fullyAwake) && + statusBarStateController.state == StatusBarState.KEYGUARD // We want to hide the notifications when collapsed too much if (collapsedEnoughToHide) { canShow = false @@ -143,7 +141,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } init { - mHeadsUpManagerPhone.addListener(this) + mHeadsUpManager.addListener(this) statusBarStateController.addCallback(this) addListener(object : WakeUpListener { override fun onFullyHiddenChanged(isFullyHidden: Boolean) { @@ -155,7 +153,7 @@ class NotificationWakeUpCoordinator @Inject constructor( increaseSpeed = false) } } - }); + }) } fun setStackScroller(stackScroller: NotificationStackScrollLayout) { @@ -178,46 +176,55 @@ class NotificationWakeUpCoordinator @Inject constructor( * @param animate should this change be animated * @param increaseSpeed should the speed be increased of the animation */ - fun setNotificationsVisibleForExpansion(visible: Boolean, animate: Boolean, - increaseSpeed: Boolean) { + fun setNotificationsVisibleForExpansion( + visible: Boolean, + animate: Boolean, + increaseSpeed: Boolean + ) { mNotificationsVisibleForExpansion = visible updateNotificationVisibility(animate, increaseSpeed) if (!visible && mNotificationsVisible) { // If we stopped expanding and we're still visible because we had a pulse that hasn't // times out, let's release them all to make sure were not stuck in a state where // notifications are visible - mHeadsUpManagerPhone.releaseAllImmediately() + mHeadsUpManager.releaseAllImmediately() } } fun addListener(listener: WakeUpListener) { - wakeUpListeners.add(listener); + wakeUpListeners.add(listener) } fun removeListener(listener: WakeUpListener) { - wakeUpListeners.remove(listener); + wakeUpListeners.remove(listener) } - private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) { + private fun updateNotificationVisibility( + animate: Boolean, + increaseSpeed: Boolean + ) { // TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore - var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications() + var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications() visible = visible && canShowPulsingHuns if (!visible && mNotificationsVisible && (wakingUp || willWakeUp) && mDozeAmount != 0.0f) { // let's not make notifications invisible while waking up, otherwise the animation // is strange - return; + return } setNotificationsVisible(visible, animate, increaseSpeed) } - private fun setNotificationsVisible(visible: Boolean, animate: Boolean, - increaseSpeed: Boolean) { + private fun setNotificationsVisible( + visible: Boolean, + animate: Boolean, + increaseSpeed: Boolean + ) { if (mNotificationsVisible == visible) { return } mNotificationsVisible = visible - mVisibilityAnimator?.cancel(); + mVisibilityAnimator?.cancel() if (animate) { notifyAnimationStart(visible) startVisibilityAnimation(increaseSpeed) @@ -230,8 +237,8 @@ class NotificationWakeUpCoordinator @Inject constructor( if (updateDozeAmountIfBypass()) { return } - if (linear != 1.0f && linear != 0.0f - && (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) { + if (linear != 1.0f && linear != 0.0f && + (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) { // Let's notify the scroller that an animation started notifyAnimationStart(mLinearDozeAmount == 1.0f) } @@ -245,17 +252,17 @@ class NotificationWakeUpCoordinator @Inject constructor( mStackScroller.setDozeAmount(mDozeAmount) updateHideAmount() if (changed && linear == 0.0f) { - setNotificationsVisible(visible = false, animate = false, increaseSpeed = false); + setNotificationsVisible(visible = false, animate = false, increaseSpeed = false) setNotificationsVisibleForExpansion(visible = false, animate = false, increaseSpeed = false) } } override fun onStateChanged(newState: Int) { - updateDozeAmountIfBypass(); + updateDozeAmountIfBypass() if (bypassController.bypassEnabled && - newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED - && (!statusBarStateController.isDozing || shouldAnimateVisibility())) { + newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED && + (!statusBarStateController.isDozing || shouldAnimateVisibility())) { // We're leaving shade locked. Let's animate the notifications away setNotificationsVisible(visible = true, increaseSpeed = false, animate = false) setNotificationsVisible(visible = false, increaseSpeed = false, animate = true) @@ -266,23 +273,23 @@ class NotificationWakeUpCoordinator @Inject constructor( override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) { val collapsedEnough = expansion <= 0.9f if (collapsedEnough != this.collapsedEnoughToHide) { - val couldShowPulsingHuns = canShowPulsingHuns; + val couldShowPulsingHuns = canShowPulsingHuns this.collapsedEnoughToHide = collapsedEnough if (couldShowPulsingHuns && !canShowPulsingHuns) { updateNotificationVisibility(animate = true, increaseSpeed = true) - mHeadsUpManagerPhone.releaseAllImmediately() + mHeadsUpManager.releaseAllImmediately() } } } private fun updateDozeAmountIfBypass(): Boolean { if (bypassController.bypassEnabled) { - var amount = 1.0f; - if (statusBarStateController.state == StatusBarState.SHADE - || statusBarStateController.state == StatusBarState.SHADE_LOCKED) { - amount = 0.0f; + var amount = 1.0f + if (statusBarStateController.state == StatusBarState.SHADE || + statusBarStateController.state == StatusBarState.SHADE_LOCKED) { + amount = 0.0f } - setDozeAmount(amount, amount) + setDozeAmount(amount, amount) return true } return false @@ -300,7 +307,7 @@ class NotificationWakeUpCoordinator @Inject constructor( visibilityAnimator.setInterpolator(Interpolators.LINEAR) var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong() if (increaseSpeed) { - duration = (duration.toFloat() / 1.5F).toLong(); + duration = (duration.toFloat() / 1.5F).toLong() } visibilityAnimator.setDuration(duration) visibilityAnimator.start() @@ -311,7 +318,7 @@ class NotificationWakeUpCoordinator @Inject constructor( mLinearVisibilityAmount = visibilityAmount mVisibilityAmount = mVisibilityInterpolator.getInterpolation( visibilityAmount) - handleAnimationFinished(); + handleAnimationFinished() updateHideAmount() } @@ -322,7 +329,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } } - fun getWakeUpHeight() : Float { + fun getWakeUpHeight(): Float { return mStackScroller.wakeUpHeight } @@ -330,7 +337,7 @@ class NotificationWakeUpCoordinator @Inject constructor( val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount) val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount) mStackScroller.setHideAmount(linearAmount, amount) - notificationsFullyHidden = linearAmount == 1.0f; + notificationsFullyHidden = linearAmount == 1.0f } private fun notifyAnimationStart(awake: Boolean) { @@ -361,7 +368,7 @@ class NotificationWakeUpCoordinator @Inject constructor( // if we animate, we see the shelf briefly visible. Instead we fully animate // the notification and its background out animate = false - } else if (!wakingUp && !willWakeUp){ + } else if (!wakingUp && !willWakeUp) { // TODO: look that this is done properly and not by anyone else entry.setHeadsUpAnimatingAway(true) mEntrySetToClearWhenFinished.add(entry) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index e8a62e48e75e..4beeedecfdf5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -37,8 +37,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.NotificationClicker; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; import com.android.systemui.statusbar.notification.row.NotifBindPipeline; @@ -66,7 +66,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private static final String TAG = "NotificationViewManager"; - private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; + private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final Context mContext; private final NotifBindPipeline mNotifBindPipeline; @@ -97,7 +97,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { StatusBarStateController statusBarStateController, NotificationGroupManager notificationGroupManager, NotificationGutsManager notificationGutsManager, - NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationInterruptStateProvider notificationInterruptionStateProvider, Provider<RowInflaterTask> rowInflaterTaskProvider, ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder) { mContext = context; @@ -106,7 +106,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { mMessagingUtil = notificationMessagingUtil; mNotificationRemoteInputManager = notificationRemoteInputManager; mNotificationLockscreenUserManager = notificationLockscreenUserManager; - mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; + mNotificationInterruptStateProvider = notificationInterruptionStateProvider; mRowInflaterTaskProvider = rowInflaterTaskProvider; mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder; } @@ -243,7 +243,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp); params.setUseLowPriority(entry.isAmbient()); - if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) { + if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) { params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP); } //TODO: Replace this API with RowContentBindParams directly diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index e425ee951ba9..1d8e9799fa8b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -31,10 +31,8 @@ import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController; -import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; @@ -44,6 +42,9 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl; import com.android.systemui.statusbar.notification.init.NotificationsControllerStub; +import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl; @@ -58,6 +59,7 @@ import java.util.concurrent.Executor; import javax.inject.Singleton; +import dagger.Binds; import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -127,7 +129,7 @@ public interface NotificationsModule { NotificationRemoteInputManager remoteInputManager, VisualStabilityManager visualStabilityManager, StatusBarStateController statusBarStateController, - NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationInterruptStateProvider notificationInterruptStateProvider, NotificationListener notificationListener, HeadsUpManager headsUpManager) { return new NotificationAlertingManager( @@ -135,7 +137,7 @@ public interface NotificationsModule { remoteInputManager, visualStabilityManager, statusBarStateController, - notificationInterruptionStateProvider, + notificationInterruptStateProvider, notificationListener, headsUpManager); } @@ -210,4 +212,9 @@ public interface NotificationsModule { NotificationEntryManager entryManager) { return featureFlags.isNewNotifPipelineRenderingEnabled() ? pipeline.get() : entryManager; } + + /** */ + @Binds + NotificationInterruptStateProvider bindNotificationInterruptStateProvider( + NotificationInterruptStateProviderImpl notificationInterruptStateProviderImpl); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt index 269a7a59f1b7..88888d10e283 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.statusbar.notification +package com.android.systemui.statusbar.notification.interruption import android.content.Context import android.media.MediaMetadata @@ -24,6 +24,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java index df21f0b21ec1..b5725029450d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification; +package com.android.systemui.statusbar.notification.interruption; import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP; @@ -27,6 +27,9 @@ import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.notification.NotificationEntryListener; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -39,7 +42,7 @@ public class NotificationAlertingManager { private final NotificationRemoteInputManager mRemoteInputManager; private final VisualStabilityManager mVisualStabilityManager; private final StatusBarStateController mStatusBarStateController; - private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; + private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final NotificationListener mNotificationListener; private HeadsUpManager mHeadsUpManager; @@ -52,13 +55,13 @@ public class NotificationAlertingManager { NotificationRemoteInputManager remoteInputManager, VisualStabilityManager visualStabilityManager, StatusBarStateController statusBarStateController, - NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationInterruptStateProvider notificationInterruptionStateProvider, NotificationListener notificationListener, HeadsUpManager headsUpManager) { mRemoteInputManager = remoteInputManager; mVisualStabilityManager = visualStabilityManager; mStatusBarStateController = statusBarStateController; - mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; + mNotificationInterruptStateProvider = notificationInterruptionStateProvider; mNotificationListener = notificationListener; mHeadsUpManager = headsUpManager; @@ -94,7 +97,7 @@ public class NotificationAlertingManager { if (entry.getRow().getPrivateLayout().getHeadsUpChild() != null) { // Possible for shouldHeadsUp to change between the inflation starting and ending. // If it does and we no longer need to heads up, we should free the view. - if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) { + if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) { mHeadsUpManager.showNotification(entry); if (!mStatusBarStateController.isDozing()) { // Mark as seen immediately @@ -109,7 +112,7 @@ public class NotificationAlertingManager { private void updateAlertState(NotificationEntry entry) { boolean alertAgain = alertAgain(entry, entry.getSbn().getNotification()); // includes check for whether this notification should be filtered: - boolean shouldAlert = mNotificationInterruptionStateProvider.shouldHeadsUp(entry); + boolean shouldAlert = mNotificationInterruptStateProvider.shouldHeadsUp(entry); final boolean wasAlerting = mHeadsUpManager.isAlerting(entry.getKey()); if (wasAlerting) { if (shouldAlert) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java new file mode 100644 index 000000000000..3292a8fcdb50 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.interruption; + +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +/** + * Provides bubble-up and heads-up state for notification entries. + * + * When a notification is heads-up when dozing, this is also called "pulsing." + */ +public interface NotificationInterruptStateProvider { + /** + * If the device is awake (not dozing): + * Whether the notification should peek in from the top and alert the user. + * + * If the device is dozing: + * Whether the notification should show the ambient view of the notification ("pulse"). + * + * @param entry the entry to check + * @return true if the entry should heads up, false otherwise + */ + boolean shouldHeadsUp(NotificationEntry entry); + + /** + * Whether the notification should appear as a bubble with a fly-out on top of the screen. + * + * @param entry the entry to check + * @return true if the entry should bubble up, false otherwise + */ + boolean shouldBubbleUp(NotificationEntry entry); + + /** + * Whether to launch the entry's full screen intent when the entry is added. + * + * @param entry the entry that was added + * @return {@code true} if we should launch the full screen intent + */ + boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry); + + /** + * Add a component that can suppress visual interruptions. + */ + void addSuppressor(NotificationInterruptSuppressor suppressor); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index bbf2dde80040..46d50441c06b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -14,33 +14,35 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification; +package com.android.systemui.statusbar.notification.interruption; import static com.android.systemui.statusbar.StatusBarState.SHADE; import android.app.NotificationManager; -import android.content.Context; +import android.content.ContentResolver; import android.database.ContentObserver; import android.hardware.display.AmbientDisplayConfiguration; +import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; -import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.HeadsUpManager; +import java.util.ArrayList; +import java.util.List; + import javax.inject.Inject; import javax.inject.Singleton; @@ -48,120 +50,84 @@ import javax.inject.Singleton; * Provides heads-up and pulsing state for notification entries. */ @Singleton -public class NotificationInterruptionStateProvider { - +public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider { private static final String TAG = "InterruptionStateProvider"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; //false; private static final boolean DEBUG_HEADS_UP = true; private static final boolean ENABLE_HEADS_UP = true; private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up"; + private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>(); private final StatusBarStateController mStatusBarStateController; private final NotificationFilter mNotificationFilter; - private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; - - private final Context mContext; + private final ContentResolver mContentResolver; private final PowerManager mPowerManager; private final IDreamManager mDreamManager; + private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; private final BatteryController mBatteryController; - - private NotificationPresenter mPresenter; + private final ContentObserver mHeadsUpObserver; private HeadsUpManager mHeadsUpManager; - private HeadsUpSuppressor mHeadsUpSuppressor; - private ContentObserver mHeadsUpObserver; @VisibleForTesting protected boolean mUseHeadsUp = false; - private boolean mDisableNotificationAlerts; @Inject - public NotificationInterruptionStateProvider(Context context, NotificationFilter filter, - StatusBarStateController stateController, BatteryController batteryController) { - this(context, - (PowerManager) context.getSystemService(Context.POWER_SERVICE), - IDreamManager.Stub.asInterface( - ServiceManager.checkService(DreamService.DREAM_SERVICE)), - new AmbientDisplayConfiguration(context), - filter, - batteryController, - stateController); - } - - @VisibleForTesting - protected NotificationInterruptionStateProvider( - Context context, + public NotificationInterruptStateProviderImpl( + ContentResolver contentResolver, PowerManager powerManager, IDreamManager dreamManager, AmbientDisplayConfiguration ambientDisplayConfiguration, NotificationFilter notificationFilter, BatteryController batteryController, - StatusBarStateController statusBarStateController) { - mContext = context; + StatusBarStateController statusBarStateController, + HeadsUpManager headsUpManager, + @Main Handler mainHandler) { + mContentResolver = contentResolver; mPowerManager = powerManager; mDreamManager = dreamManager; mBatteryController = batteryController; mAmbientDisplayConfiguration = ambientDisplayConfiguration; mNotificationFilter = notificationFilter; mStatusBarStateController = statusBarStateController; - } - - /** Sets up late-binding dependencies for this component. */ - public void setUpWithPresenter( - NotificationPresenter notificationPresenter, - HeadsUpManager headsUpManager, - HeadsUpSuppressor headsUpSuppressor) { - setUpWithPresenter(notificationPresenter, headsUpManager, headsUpSuppressor, - new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) { - @Override - public void onChange(boolean selfChange) { - boolean wasUsing = mUseHeadsUp; - mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts - && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, - Settings.Global.HEADS_UP_OFF); - Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); - if (wasUsing != mUseHeadsUp) { - if (!mUseHeadsUp) { - Log.d(TAG, - "dismissing any existing heads up notification on disable" - + " event"); - mHeadsUpManager.releaseAllImmediately(); - } - } - } - }); - } - - /** Sets up late-binding dependencies for this component. */ - public void setUpWithPresenter( - NotificationPresenter notificationPresenter, - HeadsUpManager headsUpManager, - HeadsUpSuppressor headsUpSuppressor, - ContentObserver observer) { - mPresenter = notificationPresenter; mHeadsUpManager = headsUpManager; - mHeadsUpSuppressor = headsUpSuppressor; - mHeadsUpObserver = observer; + mHeadsUpObserver = new ContentObserver(mainHandler) { + @Override + public void onChange(boolean selfChange) { + boolean wasUsing = mUseHeadsUp; + mUseHeadsUp = ENABLE_HEADS_UP + && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( + mContentResolver, + Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, + Settings.Global.HEADS_UP_OFF); + Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); + if (wasUsing != mUseHeadsUp) { + if (!mUseHeadsUp) { + Log.d(TAG, "dismissing any existing heads up notification on " + + "disable event"); + mHeadsUpManager.releaseAllImmediately(); + } + } + } + }; if (ENABLE_HEADS_UP) { - mContext.getContentResolver().registerContentObserver( + mContentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true, mHeadsUpObserver); - mContext.getContentResolver().registerContentObserver( + mContentResolver.registerContentObserver( Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true, mHeadsUpObserver); } mHeadsUpObserver.onChange(true); // set up } - /** - * Whether the notification should appear as a bubble with a fly-out on top of the screen. - * - * @param entry the entry to check - * @return true if the entry should bubble up, false otherwise - */ + @Override + public void addSuppressor(NotificationInterruptSuppressor suppressor) { + mSuppressors.add(suppressor); + } + + @Override public boolean shouldBubbleUp(NotificationEntry entry) { final StatusBarNotification sbn = entry.getSbn(); @@ -201,12 +167,8 @@ public class NotificationInterruptionStateProvider { return true; } - /** - * Whether the notification should peek in from the top and alert the user. - * - * @param entry the entry to check - * @return true if the entry should heads up, false otherwise - */ + + @Override public boolean shouldHeadsUp(NotificationEntry entry) { if (mStatusBarStateController.isDozing()) { return shouldHeadsUpWhenDozing(entry); @@ -215,6 +177,17 @@ public class NotificationInterruptionStateProvider { } } + /** + * When an entry was added, should we launch its fullscreen intent? Examples are Alarms or + * incoming calls. + */ + @Override + public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) { + return entry.getSbn().getNotification().fullScreenIntent != null + && (!shouldHeadsUp(entry) + || mStatusBarStateController.getState() == StatusBarState.KEYGUARD); + } + private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); @@ -271,13 +244,15 @@ public class NotificationInterruptionStateProvider { return false; } - if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) { - if (DEBUG_HEADS_UP) { - Log.d(TAG, "No heads up: aborted by suppressor: " + sbn.getKey()); + for (int i = 0; i < mSuppressors.size(); i++) { + if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) { + if (DEBUG_HEADS_UP) { + Log.d(TAG, "No heads up: aborted by suppressor: " + + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey()); + } + return false; } - return false; } - return true; } @@ -325,7 +300,7 @@ public class NotificationInterruptionStateProvider { } return false; } - return true; + return true; } /** @@ -334,8 +309,7 @@ public class NotificationInterruptionStateProvider { * @param entry the entry to check * @return true if these checks pass, false if the notification should not alert */ - @VisibleForTesting - public boolean canAlertCommon(NotificationEntry entry) { + private boolean canAlertCommon(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); if (mNotificationFilter.shouldFilterOut(entry)) { @@ -352,6 +326,16 @@ public class NotificationInterruptionStateProvider { } return false; } + + for (int i = 0; i < mSuppressors.size(); i++) { + if (mSuppressors.get(i).suppressInterruptions(entry)) { + if (DEBUG_HEADS_UP) { + Log.d(TAG, "No alerting: aborted by suppressor: " + + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey()); + } + return false; + } + } return true; } @@ -361,15 +345,17 @@ public class NotificationInterruptionStateProvider { * @param entry the entry to check * @return true if these checks pass, false if the notification should not alert */ - @VisibleForTesting - public boolean canAlertAwakeCommon(NotificationEntry entry) { + private boolean canAlertAwakeCommon(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); - if (mPresenter.isDeviceInVrMode()) { - if (DEBUG_HEADS_UP) { - Log.d(TAG, "No alerting: no huns or vr mode"); + for (int i = 0; i < mSuppressors.size(); i++) { + if (mSuppressors.get(i).suppressAwakeInterruptions(entry)) { + if (DEBUG_HEADS_UP) { + Log.d(TAG, "No alerting: aborted by suppressor: " + + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey()); + } + return false; } - return false; } if (isSnoozedPackage(sbn)) { @@ -392,54 +378,4 @@ public class NotificationInterruptionStateProvider { private boolean isSnoozedPackage(StatusBarNotification sbn) { return mHeadsUpManager.isSnoozed(sbn.getPackageName()); } - - /** Sets whether to disable all alerts. */ - public void setDisableNotificationAlerts(boolean disableNotificationAlerts) { - mDisableNotificationAlerts = disableNotificationAlerts; - mHeadsUpObserver.onChange(true); - } - - /** Whether all alerts are disabled. */ - @VisibleForTesting - public boolean areNotificationAlertsDisabled() { - return mDisableNotificationAlerts; - } - - /** Whether HUNs should be used. */ - @VisibleForTesting - public boolean getUseHeadsUp() { - return mUseHeadsUp; - } - - protected NotificationPresenter getPresenter() { - return mPresenter; - } - - /** - * When an entry was added, should we launch its fullscreen intent? Examples are Alarms or - * incoming calls. - * - * @param entry the entry that was added - * @return {@code true} if we should launch the full screen intent - */ - public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) { - return entry.getSbn().getNotification().fullScreenIntent != null - && (!shouldHeadsUp(entry) - || mStatusBarStateController.getState() == StatusBarState.KEYGUARD); - } - - /** A component which can suppress heads-up notifications due to the overall state of the UI. */ - public interface HeadsUpSuppressor { - /** - * Returns false if the provided notification is ineligible for heads-up according to this - * component. - * - * @param entry entry of the notification that might be heads upped - * @param sbn notification that might be heads upped - * @return false if the notification can not be heads upped - */ - boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn); - - } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java new file mode 100644 index 000000000000..c19f8bd1994a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.interruption; + +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +/** A component which can suppress visual interruptions of notifications such as heads-up and + * bubble-up. + */ +public interface NotificationInterruptSuppressor { + /** + * A unique name to identify this suppressor. + */ + default String getName() { + return this.getClass().getName(); + } + + /** + * Returns true if the provided notification is, when the device is awake, ineligible for + * heads-up according to this component. + * + * @param entry entry of the notification that might heads-up + * @return true if the heads up interruption should be suppressed when the device is awake + */ + default boolean suppressAwakeHeadsUp(NotificationEntry entry) { + return false; + } + + /** + * Returns true if the provided notification is, when the device is awake, ineligible for + * heads-up or bubble-up according to this component. + * + * @param entry entry of the notification that might heads-up or bubble-up + * @return true if interruptions should be suppressed when the device is awake + */ + default boolean suppressAwakeInterruptions(NotificationEntry entry) { + return false; + } + + /** + * Returns true if the provided notification is, regardless of awake/dozing state, + * ineligible for heads-up or bubble-up according to this component. + * + * @param entry entry of the notification that might heads-up or bubble-up + * @return true if interruptions should be suppressed + */ + default boolean suppressInterruptions(NotificationEntry entry) { + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt index 597bdb9f2e74..be3873a5fd77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt @@ -83,7 +83,7 @@ class PeopleNotificationIdentifierImpl @Inject constructor( private val Ranking.personTypeInfo get() = when { - channel.isImportantConversation -> TYPE_IMPORTANT_PERSON + channel?.isImportantConversation == true -> TYPE_IMPORTANT_PERSON isConversation -> TYPE_PERSON else -> TYPE_NON_PERSON } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 9a4e789a2e03..f61fe9830939 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -31,7 +31,6 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Notification; import android.app.NotificationChannel; import android.content.Context; import android.content.pm.PackageInfo; @@ -151,7 +150,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private int mNotificationMinHeight; private int mNotificationMinHeightLarge; private int mNotificationMinHeightMedia; - private int mNotificationMinHeightMessaging; private int mNotificationMaxHeight; private int mIncreasedPaddingBetweenElements; private int mNotificationLaunchHeight; @@ -640,16 +638,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView && expandedView.findViewById(com.android.internal.R.id.media_actions) != null; boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar(); - Class<? extends Notification.Style> style = - mEntry.getSbn().getNotification().getNotificationStyle(); - boolean isMessagingLayout = Notification.MessagingStyle.class.equals(style); - if (customView && beforeP && !mIsSummaryWithChildren) { minHeight = beforeN ? mNotificationMinHeightBeforeN : mNotificationMinHeightBeforeP; } else if (isMediaLayout && showCompactMediaSeekbar) { minHeight = mNotificationMinHeightMedia; - } else if (isMessagingLayout) { - minHeight = mNotificationMinHeightMessaging; } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) { minHeight = mNotificationMinHeightLarge; } else { @@ -1057,19 +1049,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return getShowingLayout().getVisibleNotificationHeader(); } - - /** - * @return the contracted notification header. This can be different from - * {@link #getNotificationHeader()} and also {@link #getVisibleNotificationHeader()} and only - * returns the contracted version. - */ - public NotificationHeaderView getContractedNotificationHeader() { - if (mIsSummaryWithChildren) { - return mChildrenContainer.getHeaderView(); - } - return mPrivateLayout.getContractedNotificationHeader(); - } - public void setLongPressListener(LongPressListener longPressListener) { mLongPressListener = longPressListener; } @@ -1654,8 +1633,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView R.dimen.notification_min_height_increased); mNotificationMinHeightMedia = NotificationUtils.getFontScaledHeight(mContext, R.dimen.notification_min_height_media); - mNotificationMinHeightMessaging = NotificationUtils.getFontScaledHeight(mContext, - R.dimen.notification_min_height_messaging); mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext, R.dimen.notification_max_height); mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 6dd4ff9235c4..91cf285f2262 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -25,6 +25,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; +import android.content.ContextWrapper; +import android.content.pm.ApplicationInfo; import android.os.AsyncTask; import android.os.CancellationSignal; import android.service.notification.StatusBarNotification; @@ -716,6 +718,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder sbn.getNotification()); Context packageContext = sbn.getPackageContext(mContext); + if (recoveredBuilder.usesTemplate()) { + // For all of our templates, we want it to be RTL + packageContext = new RtlEnabledContext(packageContext); + } Notification notification = sbn.getNotification(); if (notification.isMediaNotification()) { MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext, @@ -782,6 +788,19 @@ public class NotificationContentInflater implements NotificationRowContentBinder // try to purge unnecessary cached entries. mRow.getImageResolver().purgeCache(); } + + private class RtlEnabledContext extends ContextWrapper { + private RtlEnabledContext(Context packageContext) { + super(packageContext); + } + + @Override + public ApplicationInfo getApplicationInfo() { + ApplicationInfo applicationInfo = super.getApplicationInfo(); + applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL; + return applicationInfo; + } + } } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 27fd1b2c5aed..8b8a9012cbdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -1492,13 +1492,6 @@ public class NotificationContentView extends FrameLayout { } } - public NotificationHeaderView getContractedNotificationHeader() { - if (mContractedChild != null) { - return mContractedWrapper.getNotificationHeader(); - } - return null; - } - public NotificationHeaderView getVisibleNotificationHeader() { NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType); return wrapper == null ? null : wrapper.getNotificationHeader(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt new file mode 100644 index 000000000000..1e2571b5c801 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.notification.row.wrapper + +import android.content.Context +import android.view.View + +import com.android.internal.widget.ConversationLayout +import com.android.internal.widget.MessagingLinearLayout +import com.android.systemui.R +import com.android.systemui.statusbar.notification.NotificationUtils +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow + +/** + * Wraps a notification containing a converation template + */ +class NotificationConversationTemplateViewWrapper constructor( + ctx: Context, + view: View, + row: ExpandableNotificationRow +) + : NotificationTemplateViewWrapper(ctx, view, row) { + + private val minHeightWithActions: Int + private val conversationLayout: ConversationLayout + private var conversationIcon: View? = null + private var conversationBadge: View? = null + private var expandButton: View? = null + private var messagingLinearLayout: MessagingLinearLayout? = null + + init { + conversationLayout = view as ConversationLayout + minHeightWithActions = NotificationUtils.getFontScaledHeight(ctx, + R.dimen.notification_messaging_actions_min_height) + } + + private fun resolveViews() { + messagingLinearLayout = conversationLayout.messagingLinearLayout + conversationIcon = conversationLayout.requireViewById( + com.android.internal.R.id.conversation_icon) + conversationBadge = conversationLayout.requireViewById( + com.android.internal.R.id.conversation_icon_badge) + expandButton = conversationLayout.requireViewById( + com.android.internal.R.id.expand_button) + } + + override fun onContentUpdated(row: ExpandableNotificationRow) { + // Reinspect the notification. Before the super call, because the super call also updates + // the transformation types and we need to have our values set by then. + resolveViews() + super.onContentUpdated(row) + } + + override fun updateTransformedTypes() { + // This also clears the existing types + super.updateTransformedTypes() + messagingLinearLayout?.let { + mTransformationHelper.addTransformedView(it.id, it) + } + conversationIcon?.let { + mTransformationHelper.addViewTransformingToSimilar(it.id, it) + } + conversationBadge?.let { + mTransformationHelper.addViewTransformingToSimilar(it.id, it) + } + expandButton?.let { + mTransformationHelper.addViewTransformingToSimilar(it.id, it) + } + } + + override fun setRemoteInputVisible(visible: Boolean) { + conversationLayout.showHistoricMessages(visible) + } + + override fun updateExpandability(expandable: Boolean, onClickListener: View.OnClickListener?) { + conversationLayout.updateExpandability(expandable, onClickListener) + } + + override fun getMinLayoutHeight(): Int { + if (mActionsContainer != null && mActionsContainer.visibility != View.GONE) { + return minHeightWithActions + } else { + return super.getMinLayoutHeight() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 5e52c0a5a66f..1d061989a84c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -53,12 +53,13 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { protected final ViewTransformationHelper mTransformationHelper; protected int mColor; - private ImageView mIcon; + private ImageView mIcon; private NotificationExpandButton mExpandButton; protected NotificationHeaderView mNotificationHeader; private TextView mHeaderText; private ImageView mWorkProfileImage; + private boolean mIsLowPriority; private boolean mTransformLowPriorityTitle; private boolean mShowExpandButtonAtEnd; @@ -105,12 +106,16 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); - mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd); - mColor = mNotificationHeader.getOriginalIconColor(); + if (mNotificationHeader != null) { + mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd); + mColor = mNotificationHeader.getOriginalIconColor(); + } } private void addAppOpsOnClickListener(ExpandableNotificationRow row) { - mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener()); + if (mNotificationHeader != null) { + mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener()); + } } @Override @@ -127,9 +132,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { updateCropToPaddingForImageViews(); Notification notification = row.getEntry().getSbn().getNotification(); mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon()); - // The work profile image is always the same lets just set the icon tag for it not to - // animate - mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon()); + if (mWorkProfileImage != null) { + // The work profile image is always the same lets just set the icon tag for it not to + // animate + mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon()); + } // We need to reset all views that are no longer transforming in case a view was previously // transformed, but now we decided to transform its container instead. @@ -174,8 +181,9 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { protected void updateTransformedTypes() { mTransformationHelper.reset(); - mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon); - if (mIsLowPriority) { + mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, + mIcon); + if (mIsLowPriority && mHeaderText != null) { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mHeaderText); } @@ -184,7 +192,9 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { @Override public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) { mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE); - mNotificationHeader.setOnClickListener(expandable ? onClickListener : null); + if (mNotificationHeader != null) { + mNotificationHeader.setOnClickListener(expandable ? onClickListener : null); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java index 0a1a2fe3ee54..d41f5af6c524 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java @@ -353,8 +353,12 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp @Override public void setHeaderVisibleAmount(float headerVisibleAmount) { super.setHeaderVisibleAmount(headerVisibleAmount); - mNotificationHeader.setAlpha(headerVisibleAmount); - mHeaderTranslation = (1.0f - headerVisibleAmount) * mFullHeaderTranslation; + float headerTranslation = 0f; + if (mNotificationHeader != null) { + mNotificationHeader.setAlpha(headerVisibleAmount); + headerTranslation = (1.0f - headerVisibleAmount) * mFullHeaderTranslation; + } + mHeaderTranslation = headerTranslation; mView.setTranslationY(mHeaderTranslation); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index c2eff8a6a776..c834e4b376ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -35,6 +35,7 @@ import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ContrastColorUtil; +import com.android.internal.widget.ConversationLayout; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.TransformState; @@ -61,6 +62,9 @@ public abstract class NotificationViewWrapper implements TransformableView { return new NotificationMediaTemplateViewWrapper(ctx, v, row); } else if ("messaging".equals(v.getTag())) { return new NotificationMessagingTemplateViewWrapper(ctx, v, row); + } else if ("conversation".equals(v.getTag())) { + return new NotificationConversationTemplateViewWrapper(ctx, (ConversationLayout) v, + row); } Class<? extends Notification.Style> style = row.getEntry().getSbn().getNotification().getNotificationStyle(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index ee313001bfc4..90bc075b399d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -492,7 +492,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL try { result = ActivityTaskManager.getService().startActivityAsUser( null, getContext().getBasePackageName(), - getContext().getFeatureId(), intent, + getContext().getAttributionTag(), intent, intent.resolveTypeIfNeeded(getContext().getContentResolver()), null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(), UserHandle.CURRENT.getIdentifier()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index c5c3fff7d1af..c54fa2928a90 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -27,6 +27,8 @@ import android.widget.TextView; */ public class KeyguardIndicationTextView extends TextView { + private CharSequence mText = ""; + public KeyguardIndicationTextView(Context context) { super(context); } @@ -53,10 +55,12 @@ public class KeyguardIndicationTextView extends TextView { // TODO: Animation, make sure that we will show one indication long enough. if (TextUtils.isEmpty(text)) { + mText = ""; setVisibility(View.INVISIBLE); - } else { + } else if (!TextUtils.equals(text, mText)) { + mText = text; setVisibility(View.VISIBLE); - setText(text); + setText(mText); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index f38d416a6f4c..596a607bb8ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; -import android.annotation.Nullable; import android.app.StatusBarManager; import android.graphics.RectF; import android.hardware.display.AmbientDisplayConfiguration; @@ -44,7 +43,7 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationShadeWindowBlurController; +import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; @@ -82,7 +81,7 @@ public class NotificationShadeWindowViewController { private final CommandQueue mCommandQueue; private final NotificationShadeWindowView mView; private final ShadeController mShadeController; - private final NotificationShadeWindowBlurController mBlurController; + private final NotificationShadeDepthController mDepthController; private GestureDetector mGestureDetector; private View mBrightnessMirror; @@ -126,7 +125,7 @@ public class NotificationShadeWindowViewController { CommandQueue commandQueue, ShadeController shadeController, DockManager dockManager, - @Nullable NotificationShadeWindowBlurController blurController, + NotificationShadeDepthController depthController, NotificationShadeWindowView notificationShadeWindowView, NotificationPanelViewController notificationPanelViewController, SuperStatusBarViewFactory statusBarViewFactory) { @@ -149,7 +148,7 @@ public class NotificationShadeWindowViewController { mShadeController = shadeController; mDockManager = dockManager; mNotificationPanelViewController = notificationPanelViewController; - mBlurController = blurController; + mDepthController = depthController; mStatusBarViewFactory = statusBarViewFactory; // This view is not part of the newly inflated expanded status bar. @@ -394,10 +393,8 @@ public class NotificationShadeWindowViewController { mView.getContext(), mView, expandHelperCallback, dragDownCallback, mFalsingManager)); - if (mBlurController != null) { - mBlurController.setRoot(mView); - mNotificationPanelViewController.addExpansionListener(mBlurController); - } + mDepthController.setRoot(mView); + mNotificationPanelViewController.addExpansionListener(mDepthController); } public NotificationShadeWindowView getView() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index b3a62d8a4753..d343090900a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -185,15 +185,15 @@ import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; -import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.NotificationAlertingManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.init.NotificationsController; +import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; +import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -404,10 +404,9 @@ public class StatusBar extends SystemUI implements DemoMode, private final NotificationGutsManager mGutsManager; private final NotificationLogger mNotificationLogger; - private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final NotificationViewHierarchyManager mViewHierarchyManager; private final KeyguardViewMediator mKeyguardViewMediator; - private final NotificationAlertingManager mNotificationAlertingManager; + protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider; // for disabling the status bar private int mDisabled1 = 0; @@ -621,10 +620,10 @@ public class StatusBar extends SystemUI implements DemoMode, RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, - NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationInterruptStateProvider notificationInterruptStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, - NotificationAlertingManager notificationAlertingManager, + NotificationAlertingManager notificationAlertingManager, // need to inject for now DisplayMetrics displayMetrics, MetricsLogger metricsLogger, @UiBackground Executor uiBgExecutor, @@ -701,10 +700,9 @@ public class StatusBar extends SystemUI implements DemoMode, mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler; mGutsManager = notificationGutsManager; mNotificationLogger = notificationLogger; - mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; + mNotificationInterruptStateProvider = notificationInterruptStateProvider; mViewHierarchyManager = notificationViewHierarchyManager; mKeyguardViewMediator = keyguardViewMediator; - mNotificationAlertingManager = notificationAlertingManager; mDisplayMetrics = displayMetrics; mMetricsLogger = metricsLogger; mUiBgExecutor = uiBgExecutor; @@ -1238,9 +1236,9 @@ public class StatusBar extends SystemUI implements DemoMode, mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController, mHeadsUpManager, mNotificationShadeWindowView, mStackScroller, mDozeScrimController, mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController, - mNotificationAlertingManager, mKeyguardStateController, - mKeyguardIndicationController, - this /* statusBar */, mShadeController, mCommandQueue, mInitController); + mKeyguardStateController, mKeyguardIndicationController, + this /* statusBar */, mShadeController, mCommandQueue, mInitController, + mNotificationInterruptStateProvider); mNotificationShelf.setOnActivatedListener(mPresenter); mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController); @@ -1589,8 +1587,9 @@ public class StatusBar extends SystemUI implements DemoMode, } if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { - mNotificationInterruptionStateProvider.setDisableNotificationAlerts( - (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0); + if (areNotificationAlertsDisabled()) { + mHeadsUpManager.releaseAllImmediately(); + } } if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) { @@ -1605,6 +1604,10 @@ public class StatusBar extends SystemUI implements DemoMode, } } + boolean areNotificationAlertsDisabled() { + return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; + } + protected H createHandler() { return new StatusBar.H(); } @@ -2599,7 +2602,7 @@ public class StatusBar extends SystemUI implements DemoMode, } try { result = ActivityTaskManager.getService().startActivityAsUser( - null, mContext.getBasePackageName(), mContext.getFeatureId(), + null, mContext.getBasePackageName(), mContext.getAttributionTag(), intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index e1a20b6ac5d3..53fa2630a9c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -68,12 +68,12 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.HeadsUpUtil; @@ -108,7 +108,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotifCollection mNotifCollection; private final FeatureFlags mFeatureFlags; private final StatusBarStateController mStatusBarStateController; - private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; + private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final MetricsLogger mMetricsLogger; private final Context mContext; private final NotificationPanelViewController mNotificationPanel; @@ -142,7 +142,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit NotificationLockscreenUserManager lockscreenUserManager, ShadeController shadeController, StatusBar statusBar, KeyguardStateController keyguardStateController, - NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationInterruptStateProvider notificationInterruptStateProvider, MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, Handler mainThreadHandler, Handler backgroundHandler, Executor uiBgExecutor, ActivityIntentHelper activityIntentHelper, BubbleController bubbleController, @@ -167,7 +167,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mActivityStarter = activityStarter; mEntryManager = entryManager; mStatusBarStateController = statusBarStateController; - mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; + mNotificationInterruptStateProvider = notificationInterruptStateProvider; mMetricsLogger = metricsLogger; mAssistManagerLazy = assistManagerLazy; mGroupManager = groupManager; @@ -436,7 +436,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } private void handleFullScreenIntent(NotificationEntry entry) { - if (mNotificationInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) { + if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) { if (shouldSuppressFullScreenIntent(entry)) { if (DEBUG) { Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.getKey()); @@ -603,7 +603,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final ActivityIntentHelper mActivityIntentHelper; private final BubbleController mBubbleController; private NotificationPanelViewController mNotificationPanelViewController; - private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; + private NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final ShadeController mShadeController; private NotificationPresenter mNotificationPresenter; private ActivityLaunchAnimator mActivityLaunchAnimator; @@ -626,7 +626,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit NotificationGroupManager groupManager, NotificationLockscreenUserManager lockscreenUserManager, KeyguardStateController keyguardStateController, - NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationInterruptStateProvider notificationInterruptStateProvider, MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, @Main Handler mainThreadHandler, @@ -654,7 +654,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mGroupManager = groupManager; mLockscreenUserManager = lockscreenUserManager; mKeyguardStateController = keyguardStateController; - mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; + mNotificationInterruptStateProvider = notificationInterruptStateProvider; mMetricsLogger = metricsLogger; mLockPatternUtils = lockPatternUtils; mMainThreadHandler = mainThreadHandler; @@ -712,7 +712,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mShadeController, mStatusBar, mKeyguardStateController, - mNotificationInterruptionStateProvider, + mNotificationInterruptStateProvider, mMetricsLogger, mLockPatternUtils, mMainThreadHandler, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 30d6b5079166..79cea91b8612 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -60,13 +60,13 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -98,8 +98,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); private final NotificationEntryManager mEntryManager = Dependency.get(NotificationEntryManager.class); - private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider = - Dependency.get(NotificationInterruptionStateProvider.class); private final NotificationMediaManager mMediaManager = Dependency.get(NotificationMediaManager.class); private final VisualStabilityManager mVisualStabilityManager = @@ -140,13 +138,13 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, ScrimController scrimController, ActivityLaunchAnimator activityLaunchAnimator, DynamicPrivacyController dynamicPrivacyController, - NotificationAlertingManager notificationAlertingManager, KeyguardStateController keyguardStateController, KeyguardIndicationController keyguardIndicationController, StatusBar statusBar, ShadeController shadeController, CommandQueue commandQueue, - InitController initController) { + InitController initController, + NotificationInterruptStateProvider notificationInterruptStateProvider) { mContext = context; mKeyguardStateController = keyguardStateController; mNotificationPanel = panel; @@ -216,8 +214,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mEntryManager.addNotificationLifetimeExtender(mGutsManager); mEntryManager.addNotificationLifetimeExtenders( remoteInputManager.getLifetimeExtenders()); - mNotificationInterruptionStateProvider.setUpWithPresenter( - this, mHeadsUpManager, this::canHeadsUp); + notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor); mLockscreenUserManager.setUpWithPresenter(this); mMediaManager.setUpWithPresenter(this); mVisualStabilityManager.setUpWithPresenter(this); @@ -336,39 +333,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, return mEntryManager.hasActiveNotifications(); } - public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) { - if (mStatusBar.isOccluded()) { - boolean devicePublic = mLockscreenUserManager. - isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); - boolean userPublic = devicePublic - || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId()); - boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry); - if (userPublic && needsRedaction) { - // TODO(b/135046837): we can probably relax this with dynamic privacy - return false; - } - } - - if (!mCommandQueue.panelsEnabled()) { - if (DEBUG) { - Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey()); - } - return false; - } - - if (sbn.getNotification().fullScreenIntent != null) { - if (mAccessibilityManager.isTouchExplorationEnabled()) { - if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey()); - return false; - } else { - // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent - return !mKeyguardStateController.isShowing() - || mStatusBar.isOccluded(); - } - } - return true; - } - @Override public void onUserSwitched(int newUserId) { // Begin old BaseStatusBar.userSwitched @@ -507,4 +471,66 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } } }; + + private final NotificationInterruptSuppressor mInterruptSuppressor = + new NotificationInterruptSuppressor() { + @Override + public String getName() { + return TAG; + } + + @Override + public boolean suppressAwakeHeadsUp(NotificationEntry entry) { + final StatusBarNotification sbn = entry.getSbn(); + if (mStatusBar.isOccluded()) { + boolean devicePublic = mLockscreenUserManager + .isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); + boolean userPublic = devicePublic + || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId()); + boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry); + if (userPublic && needsRedaction) { + // TODO(b/135046837): we can probably relax this with dynamic privacy + return true; + } + } + + if (!mCommandQueue.panelsEnabled()) { + if (DEBUG) { + Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey()); + } + return true; + } + + if (sbn.getNotification().fullScreenIntent != null) { + // we don't allow head-up on the lockscreen (unless there's a + // "showWhenLocked" activity currently showing) if + // the potential HUN has a fullscreen intent + if (mKeyguardStateController.isShowing() && !mStatusBar.isOccluded()) { + if (DEBUG) { + Log.d(TAG, "No heads up: entry has fullscreen intent on lockscreen " + + sbn.getKey()); + } + return true; + } + + if (mAccessibilityManager.isTouchExplorationEnabled()) { + if (DEBUG) { + Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey()); + } + return true; + } + } + return false; + } + + @Override + public boolean suppressAwakeInterruptions(NotificationEntry entry) { + return isDeviceInVrMode(); + } + + @Override + public boolean suppressInterruptions(NotificationEntry entry) { + return mStatusBar.areNotificationAlertsDisabled(); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index eec8d50f00de..bbc7e7ab8c06 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -56,13 +56,13 @@ import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotificationAlertingManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; +import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; +import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.AutoHideController; @@ -139,7 +139,7 @@ public interface StatusBarPhoneModule { RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, - NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationInterruptStateProvider notificationInterruptStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, NotificationAlertingManager notificationAlertingManager, @@ -218,7 +218,7 @@ public interface StatusBarPhoneModule { remoteInputQuickSettingsDisabler, notificationGutsManager, notificationLogger, - notificationInterruptionStateProvider, + notificationInterruptStateProvider, notificationViewHierarchyManager, keyguardViewMediator, notificationAlertingManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 759bad4f77b7..812ce1c62677 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -236,7 +236,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C String action = intent.getAction(); if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) { - String tz = intent.getStringExtra("time-zone"); + String tz = intent.getStringExtra(Intent.EXTRA_TIMEZONE); handler.post(() -> { mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz)); if (mClockFormat != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index cebcf760a990..54e8e72f4364 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -577,7 +577,7 @@ public class MobileSignalController extends SignalController< } boolean isDataDisabled() { - return !mPhone.isDataConnectionEnabled(); + return !mPhone.isDataConnectionAllowed(); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index b84208c9ac35..99709402573c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -630,7 +630,7 @@ public class NetworkControllerImpl extends BroadcastReceiver @VisibleForTesting void doUpdateMobileControllers() { List<SubscriptionInfo> subscriptions = mSubscriptionManager - .getActiveAndHiddenSubscriptionInfoList(); + .getCompleteActiveSubscriptionInfoList(); if (subscriptions == null) { subscriptions = Collections.emptyList(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java index 74739e19ede9..e70e30a5ab57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java @@ -24,6 +24,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.annotation.IntDef; +import android.annotation.UiThread; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -43,7 +44,6 @@ import com.android.systemui.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; -import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import java.util.Set; @@ -98,8 +98,27 @@ class AudioRecordingDisclosureBar { private TextView mTextView; @State private int mState = STATE_NOT_SHOWN; - private final Set<String> mAudioRecordingApps = new HashSet<>(); - private final Queue<String> mPendingNotifications = new LinkedList<>(); + /** + * Set of the applications that currently are conducting audio recording. + */ + private final Set<String> mActiveAudioRecordingPackages = new ArraySet<>(); + /** + * Set of applications that we've notified the user about since the indicator came up. Meaning + * that if an application is in this list then at some point since the indicator came up, it + * was expanded showing this application's title. + * Used not to notify the user about the same application again while the indicator is shown. + * We empty this set every time the indicator goes off the screen (we always call {@code + * mSessionNotifiedPackages.clear()} before calling {@link #hide()}). + */ + private final Set<String> mSessionNotifiedPackages = new ArraySet<>(); + /** + * If an application starts recording while the TV indicator is neither in {@link + * #STATE_NOT_SHOWN} nor in {@link #STATE_MINIMIZED}, then we add the application's package + * name to the queue, from which we take packages names one by one to disclose the + * corresponding applications' titles to the user, whenever the indicator eventually comes to + * one of the two aforementioned states. + */ + private final Queue<String> mPendingNotificationPackages = new LinkedList<>(); AudioRecordingDisclosureBar(Context context) { mContext = context; @@ -115,11 +134,16 @@ class AudioRecordingDisclosureBar { new OnActiveRecordingListener()); } + @UiThread private void onStartedRecording(String packageName) { - if (!mAudioRecordingApps.add(packageName)) { + if (!mActiveAudioRecordingPackages.add(packageName)) { // This app is already known to perform recording return; } + if (!mSessionNotifiedPackages.add(packageName)) { + // We've already notified user about this app, no need to do it again. + return; + } switch (mState) { case STATE_NOT_SHOWN: @@ -137,13 +161,14 @@ class AudioRecordingDisclosureBar { case STATE_MINIMIZING: // Currently animating or expanded. Thus add to the pending notifications, and it // will be picked up once the indicator comes to the STATE_MINIMIZED. - mPendingNotifications.add(packageName); + mPendingNotificationPackages.add(packageName); break; } } + @UiThread private void onDoneRecording(String packageName) { - if (!mAudioRecordingApps.remove(packageName)) { + if (!mActiveAudioRecordingPackages.remove(packageName)) { // Was not marked as an active recorder, do nothing return; } @@ -151,11 +176,13 @@ class AudioRecordingDisclosureBar { // If not MINIMIZED, will check whether the indicator should be hidden when the indicator // comes to the STATE_MINIMIZED eventually. If is in the STATE_MINIMIZED, but there are // other active recorders - simply ignore. - if (mState == STATE_MINIMIZED && mAudioRecordingApps.isEmpty()) { + if (mState == STATE_MINIMIZED && mActiveAudioRecordingPackages.isEmpty()) { + mSessionNotifiedPackages.clear(); hide(); } } + @UiThread private void show(String packageName) { // Inflate the indicator view mIndicatorView = LayoutInflater.from(mContext).inflate( @@ -230,6 +257,7 @@ class AudioRecordingDisclosureBar { mState = STATE_APPEARING; } + @UiThread private void expand(String packageName) { final String label = getApplicationLabel(packageName); mTextView.setText(mContext.getString(R.string.app_accessed_mic, label)); @@ -253,6 +281,7 @@ class AudioRecordingDisclosureBar { mState = STATE_MAXIMIZING; } + @UiThread private void minimize() { final int targetOffset = mTextsContainers.getWidth(); final AnimatorSet set = new AnimatorSet(); @@ -274,6 +303,7 @@ class AudioRecordingDisclosureBar { mState = STATE_MINIMIZING; } + @UiThread private void hide() { final int targetOffset = mIndicatorView.getWidth() - (int) mIconTextsContainer.getTranslationX(); @@ -294,24 +324,28 @@ class AudioRecordingDisclosureBar { mState = STATE_DISAPPEARING; } + @UiThread private void onExpanded() { mState = STATE_SHOWN; mIndicatorView.postDelayed(this::minimize, MAXIMIZED_DURATION); } + @UiThread private void onMinimized() { mState = STATE_MINIMIZED; - if (!mPendingNotifications.isEmpty()) { + if (!mPendingNotificationPackages.isEmpty()) { // There is a new application that started recording, tell the user about it. - expand(mPendingNotifications.poll()); - } else if (mAudioRecordingApps.isEmpty()) { - // Nobody is recording anymore, remove the indicator. + expand(mPendingNotificationPackages.poll()); + } else if (mActiveAudioRecordingPackages.isEmpty()) { + // Nobody is recording anymore, clear state and remove the indicator. + mSessionNotifiedPackages.clear(); hide(); } } + @UiThread private void onHidden() { final WindowManager windowManager = (WindowManager) mContext.getSystemService( Context.WINDOW_SERVICE); @@ -326,8 +360,15 @@ class AudioRecordingDisclosureBar { mBgRight = null; mState = STATE_NOT_SHOWN; + + // Check if anybody started recording while we were in STATE_DISAPPEARING + if (!mPendingNotificationPackages.isEmpty()) { + // There is a new application that started recording, tell the user about it. + show(mPendingNotificationPackages.poll()); + } } + @UiThread private void startPulsatingAnimation() { final View pulsatingView = mIconTextsContainer.findViewById(R.id.pulsating_circle); final ObjectAnimator animator = diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 2452218226bb..248bdc870418 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -41,9 +41,9 @@ import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.util.leak.LeakDetector; -import java.util.HashMap; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; import javax.inject.Singleton; @@ -69,7 +69,8 @@ public class TunerServiceImpl extends TunerService { // Map of Uris we listen on to their settings keys. private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>(); // Map of settings keys to the listener. - private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>(); + private final ConcurrentHashMap<String, Set<Tunable>> mTunableLookup = + new ConcurrentHashMap<>(); // Set of all tunables, used for leak detection. private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null; private final Context mContext; diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java index 23df991ed743..ccb86999674c 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java +++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java @@ -324,7 +324,7 @@ public class SystemWindows { @Override public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, - boolean sync) {} + float zoom, boolean sync) {} @Override public void dispatchWallpaperCommand(String action, int x, int y, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java index ea6cf3329772..f7daf974b834 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java @@ -249,7 +249,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the // same answer as KeyguardUpdateMonitor. Remove when this is addressed - when(mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList()).thenReturn( + when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn( new ArrayList<>()); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn( diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index eead1204aa11..4f4ce13e41ca 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -516,7 +516,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION); list.add(TEST_SUBSCRIPTION_2); - when(mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList()).thenReturn(list); + when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list); mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged( TEST_SUBSCRIPTION_2.getSubscriptionId()); mTestableLooper.processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java index 61991816a407..353fe625b8ea 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -50,6 +50,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; + @SmallTest @RunWith(AndroidTestingRunner.class) // Need to run tests on main looper because LiveData operations such as setData, observe, @@ -126,7 +128,7 @@ public final class ClockManagerTest extends SysuiTestCase { when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(null); when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(null); // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); + mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); // THEN the result is null, indicated the default clock face should be used. assertThat(mClockManager.getCurrentClock()).isNull(); } @@ -136,7 +138,7 @@ public final class ClockManagerTest extends SysuiTestCase { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); + mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); // THEN the plugin is the bubble clock face. assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); } @@ -146,7 +148,7 @@ public final class ClockManagerTest extends SysuiTestCase { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); + mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); // THEN the plugin is the bubble clock face. ArgumentCaptor<ClockPlugin> captor = ArgumentCaptor.forClass(ClockPlugin.class); verify(mMockListener1).onClockChanged(captor.capture()); @@ -158,7 +160,7 @@ public final class ClockManagerTest extends SysuiTestCase { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); + mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); // THEN the listeners receive separate instances of the Bubble clock plugin. ArgumentCaptor<ClockPlugin> captor1 = ArgumentCaptor.forClass(ClockPlugin.class); ArgumentCaptor<ClockPlugin> captor2 = ArgumentCaptor.forClass(ClockPlugin.class); @@ -175,7 +177,7 @@ public final class ClockManagerTest extends SysuiTestCase { // custom clock face. when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn("bad value"); // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); + mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); // THEN the result is null. assertThat(mClockManager.getCurrentClock()).isNull(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 78160c406964..6e612d7124ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -45,7 +45,11 @@ import android.app.IActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.content.res.Resources; +import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.face.FaceManager; +import android.os.Handler; +import android.os.PowerManager; +import android.service.dreams.IDreamManager; import android.service.notification.ZenModeConfig; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -61,14 +65,12 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoveInterceptor; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -227,15 +229,17 @@ public class BubbleControllerTest extends SysuiTestCase { mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); - TestableNotificationInterruptionStateProvider interruptionStateProvider = - new TestableNotificationInterruptionStateProvider(mContext, + TestableNotificationInterruptStateProviderImpl interruptionStateProvider = + new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), + mock(PowerManager.class), + mock(IDreamManager.class), + mock(AmbientDisplayConfiguration.class), mock(NotificationFilter.class), mock(StatusBarStateController.class), - mock(BatteryController.class)); - interruptionStateProvider.setUpWithPresenter( - mock(NotificationPresenter.class), - mock(HeadsUpManager.class), - mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class)); + mock(BatteryController.class), + mock(HeadsUpManager.class), + mock(Handler.class) + ); mBubbleData = new BubbleData(mContext); when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false); mBubbleController = new TestableBubbleController( diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java index 5ef4cd251f11..624464401a4a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java @@ -41,7 +41,11 @@ import android.app.IActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.content.res.Resources; +import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.face.FaceManager; +import android.os.Handler; +import android.os.PowerManager; +import android.service.dreams.IDreamManager; import android.service.notification.ZenModeConfig; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -57,12 +61,10 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; @@ -212,15 +214,17 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); - TestableNotificationInterruptionStateProvider interruptionStateProvider = - new TestableNotificationInterruptionStateProvider(mContext, + TestableNotificationInterruptStateProviderImpl interruptionStateProvider = + new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), + mock(PowerManager.class), + mock(IDreamManager.class), + mock(AmbientDisplayConfiguration.class), mock(NotificationFilter.class), mock(StatusBarStateController.class), - mock(BatteryController.class)); - interruptionStateProvider.setUpWithPresenter( - mock(NotificationPresenter.class), - mock(HeadsUpManager.class), - mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class)); + mock(BatteryController.class), + mock(HeadsUpManager.class), + mock(Handler.class) + ); mBubbleData = new BubbleData(mContext); when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true); mBubbleController = new TestableBubbleController( diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java index de1fb41ddbbd..d3d90c408468 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java @@ -23,8 +23,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; @@ -44,7 +44,7 @@ public class TestableBubbleController extends BubbleController { ShadeController shadeController, BubbleData data, ConfigurationController configurationController, - NotificationInterruptionStateProvider interruptionStateProvider, + NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager lockscreenUserManager, NotificationGroupManager groupManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java new file mode 100644 index 000000000000..17dc76b38a56 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.bubbles; + +import android.content.ContentResolver; +import android.hardware.display.AmbientDisplayConfiguration; +import android.os.Handler; +import android.os.PowerManager; +import android.service.dreams.IDreamManager; + +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.HeadsUpManager; + +public class TestableNotificationInterruptStateProviderImpl + extends NotificationInterruptStateProviderImpl { + + TestableNotificationInterruptStateProviderImpl( + ContentResolver contentResolver, + PowerManager powerManager, + IDreamManager dreamManager, + AmbientDisplayConfiguration ambientDisplayConfiguration, + NotificationFilter filter, + StatusBarStateController statusBarStateController, + BatteryController batteryController, + HeadsUpManager headsUpManager, + Handler mainHandler) { + super(contentResolver, + powerManager, + dreamManager, + ambientDisplayConfiguration, + filter, + batteryController, + statusBarStateController, + headsUpManager, + mainHandler); + mUseHeadsUp = true; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java deleted file mode 100644 index 5d192b2071b5..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.bubbles; - -import android.content.Context; - -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; -import com.android.systemui.statusbar.policy.BatteryController; - -public class TestableNotificationInterruptionStateProvider - extends NotificationInterruptionStateProvider { - - TestableNotificationInterruptionStateProvider(Context context, - NotificationFilter filter, StatusBarStateController controller, - BatteryController batteryController) { - super(context, filter, controller, batteryController); - mUseHeadsUp = true; - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt index eceb1ddaaf06..c25d4e2d4b30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt @@ -22,6 +22,8 @@ import android.os.Binder import android.os.UserHandle import android.service.controls.Control import android.service.controls.DeviceTypes +import android.service.controls.IControlsSubscriber +import android.service.controls.IControlsSubscription import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -34,6 +36,8 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.`when` @@ -48,6 +52,7 @@ import org.mockito.MockitoAnnotations class ControlsBindingControllerImplTest : SysuiTestCase() { companion object { + fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() fun <T> any(): T = Mockito.any<T>() private val TEST_COMPONENT_NAME_1 = ComponentName("TEST_PKG", "TEST_CLS_1") private val TEST_COMPONENT_NAME_2 = ComponentName("TEST_PKG", "TEST_CLS_2") @@ -57,6 +62,15 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { @Mock private lateinit var mockControlsController: ControlsController + @Captor + private lateinit var subscriberCaptor: ArgumentCaptor<IControlsSubscriber.Stub> + + @Captor + private lateinit var loadSubscriberCaptor: ArgumentCaptor<IControlsSubscriber.Stub> + + @Captor + private lateinit var listStringCaptor: ArgumentCaptor<List<String>> + private val user = UserHandle.of(mContext.userId) private val otherUser = UserHandle.of(user.identifier + 1) @@ -97,6 +111,102 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { } @Test + fun testBindAndLoad_cancel() { + val callback = object : ControlsBindingController.LoadCallback { + override fun error(message: String) {} + + override fun accept(t: List<Control>) {} + } + val subscription = mock(IControlsSubscription::class.java) + + val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) + + verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor)) + loadSubscriberCaptor.value.onSubscribe(Binder(), subscription) + + canceller.run() + verify(subscription).cancel() + } + + @Test + fun testBindAndLoad_noCancelAfterOnComplete() { + val callback = object : ControlsBindingController.LoadCallback { + override fun error(message: String) {} + + override fun accept(t: List<Control>) {} + } + val subscription = mock(IControlsSubscription::class.java) + + val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) + + verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor)) + val b = Binder() + loadSubscriberCaptor.value.onSubscribe(b, subscription) + + loadSubscriberCaptor.value.onComplete(b) + canceller.run() + verify(subscription, never()).cancel() + } + + @Test + fun testLoad_onCompleteRemovesTimeout() { + val callback = object : ControlsBindingController.LoadCallback { + override fun error(message: String) {} + + override fun accept(t: List<Control>) {} + } + val subscription = mock(IControlsSubscription::class.java) + + val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) + + verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor)) + val b = Binder() + subscriberCaptor.value.onSubscribe(b, subscription) + + subscriberCaptor.value.onComplete(b) + verify(providers[0]).cancelLoadTimeout() + } + + @Test + fun testLoad_onErrorRemovesTimeout() { + val callback = object : ControlsBindingController.LoadCallback { + override fun error(message: String) {} + + override fun accept(t: List<Control>) {} + } + val subscription = mock(IControlsSubscription::class.java) + + val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) + + verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor)) + val b = Binder() + subscriberCaptor.value.onSubscribe(b, subscription) + + subscriberCaptor.value.onError(b, "") + verify(providers[0]).cancelLoadTimeout() + } + + @Test + fun testBindAndLoad_noCancelAfterOnError() { + val callback = object : ControlsBindingController.LoadCallback { + override fun error(message: String) {} + + override fun accept(t: List<Control>) {} + } + val subscription = mock(IControlsSubscription::class.java) + + val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) + + verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor)) + val b = Binder() + loadSubscriberCaptor.value.onSubscribe(b, subscription) + + loadSubscriberCaptor.value.onError(b, "") + canceller.run() + verify(subscription, never()).cancel() + } + + @Test fun testBindService() { controller.bindService(TEST_COMPONENT_NAME_1) executor.runAllReady() @@ -115,8 +225,13 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { executor.runAllReady() + val subs = mock(IControlsSubscription::class.java) verify(providers[0]).maybeBindAndSubscribe( + capture(listStringCaptor), capture(subscriberCaptor)) + assertEquals(listStringCaptor.value, listOf(controlInfo1.controlId, controlInfo2.controlId)) + + subscriberCaptor.value.onSubscribe(providers[0].token, subs) } @Test @@ -126,7 +241,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { executor.runAllReady() - verify(providers[0], never()).unsubscribe() + verify(providers[0], never()).cancelSubscription(any()) } @Test @@ -137,12 +252,21 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { StructureInfo(TEST_COMPONENT_NAME_1, "Home", listOf(controlInfo1, controlInfo2)) controller.subscribe(structure) + executor.runAllReady() - controller.unsubscribe() + val subs = mock(IControlsSubscription::class.java) + verify(providers[0]).maybeBindAndSubscribe( + capture(listStringCaptor), capture(subscriberCaptor)) + assertEquals(listStringCaptor.value, + listOf(controlInfo1.controlId, controlInfo2.controlId)) + + subscriberCaptor.value.onSubscribe(providers[0].token, subs) + executor.runAllReady() + controller.unsubscribe() executor.runAllReady() - verify(providers[0]).unsubscribe() + verify(providers[0]).cancelSubscription(subs) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index c70c56a8660b..f9c98157edb8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -346,6 +346,88 @@ class ControlsControllerImplTest : SysuiTestCase() { } @Test + fun testCancelLoad() { + val canceller = object : Runnable { + var ran = false + override fun run() { + ran = true + } + } + `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) + + var loaded = false + controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) + delayableExecutor.runAllReady() + controller.loadForComponent(TEST_COMPONENT, Consumer { + loaded = true + }) + + controller.cancelLoad() + delayableExecutor.runAllReady() + + assertFalse(loaded) + assertTrue(canceller.ran) + } + + @Test + fun testCancelLoad_noCancelAfterSuccessfulLoad() { + val canceller = object : Runnable { + var ran = false + override fun run() { + ran = true + } + } + `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) + + var loaded = false + controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) + delayableExecutor.runAllReady() + controller.loadForComponent(TEST_COMPONENT, Consumer { + loaded = true + }) + + verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), + capture(controlLoadCallbackCaptor)) + + controlLoadCallbackCaptor.value.accept(emptyList()) + + controller.cancelLoad() + delayableExecutor.runAllReady() + + assertTrue(loaded) + assertFalse(canceller.ran) + } + + @Test + fun testCancelLoad_noCancelAfterErrorLoad() { + val canceller = object : Runnable { + var ran = false + override fun run() { + ran = true + } + } + `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) + + var loaded = false + controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) + delayableExecutor.runAllReady() + controller.loadForComponent(TEST_COMPONENT, Consumer { + loaded = true + }) + + verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), + capture(controlLoadCallbackCaptor)) + + controlLoadCallbackCaptor.value.error("") + + controller.cancelLoad() + delayableExecutor.runAllReady() + + assertTrue(loaded) + assertFalse(canceller.ran) + } + + @Test fun testFavoriteInformationModifiedOnLoad() { controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) delayableExecutor.runAllReady() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt index fd92ad026312..2d3757c29ebf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt @@ -1,7 +1,7 @@ /* * Copyright (C) 2020 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (149the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * @@ -43,6 +43,7 @@ import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -83,7 +84,6 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { context, executor, actionCallbackService, - subscriberService, UserHandle.of(0), componentName ) @@ -144,9 +144,22 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { } @Test + fun testMaybeBindAndLoad_timeoutCancelled() { + manager.maybeBindAndLoad(subscriberService) + executor.runAllReady() + + manager.cancelLoadTimeout() + + executor.advanceClockToLast() + executor.runAllReady() + + verify(subscriberService, never()).onError(any(), anyString()) + } + + @Test fun testMaybeBindAndSubscribe() { val list = listOf("TEST_ID") - manager.maybeBindAndSubscribe(list) + manager.maybeBindAndSubscribe(list, subscriberService) executor.runAllReady() assertTrue(mContext.isBound(componentName)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt new file mode 100644 index 000000000000..ff5c8d44eee3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.controller + +import android.content.ComponentName +import android.os.Binder +import android.service.controls.Control +import android.service.controls.IControlsSubscription +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.`when` +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class StatefulControlSubscriberTest : SysuiTestCase() { + + @Mock + private lateinit var controller: ControlsController + + @Mock + private lateinit var subscription: IControlsSubscription + + @Mock + private lateinit var provider: ControlsProviderLifecycleManager + + @Mock + private lateinit var control: Control + + private val executor = FakeExecutor(FakeSystemClock()) + private val token = Binder() + private val badToken = Binder() + + private val TEST_COMPONENT = ComponentName("TEST_PKG", "TEST_CLS_1") + + private lateinit var scs: StatefulControlSubscriber + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + `when`(provider.componentName).thenReturn(TEST_COMPONENT) + `when`(provider.token).thenReturn(token) + scs = StatefulControlSubscriber(controller, provider, executor) + } + + @Test + fun testOnSubscribe() { + scs.onSubscribe(token, subscription) + + executor.runAllReady() + verify(provider).startSubscription(subscription) + } + + @Test + fun testOnSubscribe_badToken() { + scs.onSubscribe(badToken, subscription) + + executor.runAllReady() + verify(provider, never()).startSubscription(subscription) + } + + @Test + fun testOnNext() { + scs.onSubscribe(token, subscription) + scs.onNext(token, control) + + executor.runAllReady() + verify(controller).refreshStatus(TEST_COMPONENT, control) + } + + @Test + fun testOnNext_multiple() { + scs.onSubscribe(token, subscription) + scs.onNext(token, control) + scs.onNext(token, control) + scs.onNext(token, control) + + executor.runAllReady() + verify(controller, times(3)).refreshStatus(TEST_COMPONENT, control) + } + + @Test + fun testOnNext_noRefreshBeforeSubscribe() { + scs.onNext(token, control) + + executor.runAllReady() + verify(controller, never()).refreshStatus(TEST_COMPONENT, control) + } + + @Test + fun testOnNext_noRefreshAfterCancel() { + scs.onSubscribe(token, subscription) + executor.runAllReady() + + scs.cancel() + scs.onNext(token, control) + + executor.runAllReady() + verify(controller, never()).refreshStatus(TEST_COMPONENT, control) + } + + @Test + fun testOnNext_noRefreshAfterError() { + scs.onSubscribe(token, subscription) + scs.onError(token, "Error") + scs.onNext(token, control) + + executor.runAllReady() + verify(controller, never()).refreshStatus(TEST_COMPONENT, control) + } + + @Test + fun testOnNext_noRefreshAfterComplete() { + scs.onSubscribe(token, subscription) + scs.onComplete(token) + scs.onNext(token, control) + + executor.runAllReady() + verify(controller, never()).refreshStatus(TEST_COMPONENT, control) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 9117ea8f9fc2..f535351f0f13 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -248,31 +248,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test - public void pausingAod_softBlanks_withSpuriousSensorDuringPause() throws Exception { - mScreen.transitionTo(UNINITIALIZED, INITIALIZED); - mScreen.transitionTo(INITIALIZED, DOZE_AOD); - mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING); - mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED); - - reset(mDozeHost); - mSensor.sendSensorEvent(1); - verify(mDozeHost).setAodDimmingScrim(eq(1f)); - } - - @Test - public void screenOff_softBlanks() throws Exception { - mScreen.transitionTo(UNINITIALIZED, INITIALIZED); - mScreen.transitionTo(INITIALIZED, DOZE_AOD); - mScreen.transitionTo(DOZE_AOD, DOZE); - verify(mDozeHost).setAodDimmingScrim(eq(1f)); - - reset(mDozeHost); - mScreen.transitionTo(DOZE, DOZE_AOD); - mSensor.sendSensorEvent(2); - verify(mDozeHost).setAodDimmingScrim(eq(0f)); - } - - @Test public void pausingAod_unblanksAfterSensor() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index 00980129cf74..73f3ddd29bcf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -22,7 +22,9 @@ import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertFalse; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -137,6 +139,8 @@ public class QSTileHostTest extends SysuiTestCase { return new TestTile1(mQSTileHost); } else if ("spec2".equals(spec)) { return new TestTile2(mQSTileHost); + } else if ("na".equals(spec)) { + return new NotAvailableTile(mQSTileHost); } else if (CUSTOM_TILE_SPEC.equals(spec)) { return mCustomTile; } else { @@ -283,6 +287,12 @@ public class QSTileHostTest extends SysuiTestCase { assertEquals(1, specs.size()); } + @Test + public void testNotAvailableTile_specNotNull() { + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "na"); + verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString()); + } + private static class TestQSTileHost extends QSTileHost { TestQSTileHost(Context context, StatusBarIconController iconController, QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper, @@ -369,4 +379,16 @@ public class QSTileHostTest extends SysuiTestCase { super(host); } } + + private class NotAvailableTile extends TestTile { + + protected NotAvailableTile(QSHost host) { + super(host); + } + + @Override + public boolean isAvailable() { + return false; + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index 1693e7f4df7f..f9c62e1ab604 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.notification.interruption; import static android.app.Notification.FLAG_BUBBLE; @@ -30,15 +30,14 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; import android.app.PendingIntent; -import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; import android.hardware.display.AmbientDisplayConfiguration; +import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; import android.service.dreams.IDreamManager; @@ -50,7 +49,6 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.policy.BatteryController; @@ -68,7 +66,7 @@ import org.mockito.MockitoAnnotations; */ @RunWith(AndroidTestingRunner.class) @SmallTest -public class NotificationInterruptionStateProviderTest extends SysuiTestCase { +public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Mock PowerManager mPowerManager; @@ -81,38 +79,36 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { @Mock StatusBarStateController mStatusBarStateController; @Mock - NotificationPresenter mPresenter; - @Mock HeadsUpManager mHeadsUpManager; @Mock - NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor; - @Mock BatteryController mBatteryController; + @Mock + Handler mMockHandler; - private NotificationInterruptionStateProvider mNotifInterruptionStateProvider; + private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider; @Before public void setup() { MockitoAnnotations.initMocks(this); mNotifInterruptionStateProvider = - new TestableNotificationInterruptionStateProvider(mContext, + new NotificationInterruptStateProviderImpl( + mContext.getContentResolver(), mPowerManager, mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter, + mBatteryController, mStatusBarStateController, - mBatteryController); + mHeadsUpManager, + mMockHandler); - mNotifInterruptionStateProvider.setUpWithPresenter( - mPresenter, - mHeadsUpManager, - mHeadsUpSuppressor); + mNotifInterruptionStateProvider.mUseHeadsUp = true; } /** * Sets up the state such that any requests to - * {@link NotificationInterruptionStateProvider#canAlertCommon(NotificationEntry)} will + * {@link NotificationInterruptStateProviderImpl#canAlertCommon(NotificationEntry)} will * pass as long its provided NotificationEntry fulfills group suppression check. */ private void ensureStateForAlertCommon() { @@ -121,17 +117,16 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { /** * Sets up the state such that any requests to - * {@link NotificationInterruptionStateProvider#canAlertAwakeCommon(NotificationEntry)} will + * {@link NotificationInterruptStateProviderImpl#canAlertAwakeCommon(NotificationEntry)} will * pass as long its provided NotificationEntry fulfills launch fullscreen check. */ private void ensureStateForAlertAwakeCommon() { - when(mPresenter.isDeviceInVrMode()).thenReturn(false); when(mHeadsUpManager.isSnoozed(any())).thenReturn(false); } /** * Sets up the state such that any requests to - * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will + * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will * pass as long its provided NotificationEntry fulfills importance & DND checks. */ private void ensureStateForHeadsUpWhenAwake() throws RemoteException { @@ -141,12 +136,11 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { when(mStatusBarStateController.isDozing()).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); when(mPowerManager.isScreenOn()).thenReturn(true); - when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true); } /** * Sets up the state such that any requests to - * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will + * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will * pass as long its provided NotificationEntry fulfills importance & DND checks. */ private void ensureStateForHeadsUpWhenDozing() { @@ -158,7 +152,7 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { /** * Sets up the state such that any requests to - * {@link NotificationInterruptionStateProvider#shouldBubbleUp(NotificationEntry)} will + * {@link NotificationInterruptStateProviderImpl#shouldBubbleUp(NotificationEntry)} will * pass as long its provided NotificationEntry fulfills importance & bubble checks. */ private void ensureStateForBubbleUp() { @@ -166,75 +160,53 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { ensureStateForAlertAwakeCommon(); } - /** - * Ensure that the disabled state is set correctly. - */ @Test - public void testDisableNotificationAlerts() { - // Enabled by default - assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isFalse(); + public void testDefaultSuppressorDoesNotSuppress() { + // GIVEN a suppressor without any overrides + final NotificationInterruptSuppressor defaultSuppressor = + new NotificationInterruptSuppressor() { + @Override + public String getName() { + return "defaultSuppressor"; + } + }; - // Disable alerts - mNotifInterruptionStateProvider.setDisableNotificationAlerts(true); - assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isTrue(); + NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); - // Enable alerts - mNotifInterruptionStateProvider.setDisableNotificationAlerts(false); - assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isFalse(); + // THEN this suppressor doesn't suppress anything by default + assertThat(defaultSuppressor.suppressAwakeHeadsUp(entry)).isFalse(); + assertThat(defaultSuppressor.suppressAwakeInterruptions(entry)).isFalse(); + assertThat(defaultSuppressor.suppressInterruptions(entry)).isFalse(); } - /** - * Ensure that the disabled alert state effects whether HUNs are enabled. - */ @Test - public void testHunSettingsChange_enabled_butAlertsDisabled() { - // Set up but without a mock change observer - mNotifInterruptionStateProvider.setUpWithPresenter( - mPresenter, - mHeadsUpManager, - mHeadsUpSuppressor); - - // HUNs enabled by default - assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isTrue(); - - // Set alerts disabled - mNotifInterruptionStateProvider.setDisableNotificationAlerts(true); + public void testShouldHeadsUpAwake() throws RemoteException { + ensureStateForHeadsUpWhenAwake(); - // No more HUNs - assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isFalse(); + NotificationEntry entry = createNotification(IMPORTANCE_HIGH); + assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); } - /** - * Alerts can happen. - */ @Test - public void testCanAlertCommon_true() { - ensureStateForAlertCommon(); - - NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); - assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isTrue(); - } + public void testShouldNotHeadsUpAwake_flteredOut() throws RemoteException { + // GIVEN state for "heads up when awake" is true + ensureStateForHeadsUpWhenAwake(); - /** - * Filtered out notifications don't alert. - */ - @Test - public void testCanAlertCommon_false_filteredOut() { - ensureStateForAlertCommon(); - when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true); + // WHEN this entry should be filtered out + NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); + when(mNotificationFilter.shouldFilterOut(entry)).thenReturn(true); - NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); - assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse(); + // THEN we shouldn't heads up this entry + assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } - /** - * Grouped notifications have different alerting behaviours, sometimes the alert for a - * grouped notification may be suppressed {@link android.app.Notification#GROUP_ALERT_CHILDREN}. - */ @Test - public void testCanAlertCommon_false_suppressedForGroups() { - ensureStateForAlertCommon(); + public void testShouldNotHeadsUp_suppressedForGroups() throws RemoteException { + // GIVEN state for "heads up when awake" is true + ensureStateForHeadsUpWhenAwake(); + // WHEN the alert for a grouped notification is suppressed + // see {@link android.app.Notification#GROUP_ALERT_CHILDREN} NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") .setOpPkg("a") @@ -247,40 +219,40 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { .setImportance(IMPORTANCE_DEFAULT) .build(); - assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse(); + // THEN this entry shouldn't HUN + assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } - /** - * HUNs while dozing can happen. - */ @Test - public void testShouldHeadsUpWhenDozing_true() { + public void testShouldHeadsUpWhenDozing() { ensureStateForHeadsUpWhenDozing(); NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); } - /** - * Ambient display can show HUNs for new notifications, this may be disabled. - */ @Test - public void testShouldHeadsUpWhenDozing_false_pulseDisabled() { + public void testShouldNotHeadsUpWhenDozing_pulseDisabled() { + // GIVEN state for "heads up when dozing" is true ensureStateForHeadsUpWhenDozing(); + + // WHEN pulsing (HUNs when dozing) is disabled when(mAmbientDisplayConfiguration.pulseOnNotificationEnabled(anyInt())).thenReturn(false); + // THEN this entry shouldn't HUN NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } - /** - * If the device is not in ambient display or sleeping then we don't HUN. - */ @Test - public void testShouldHeadsUpWhenDozing_false_notDozing() { + public void testShouldNotHeadsUpWhenDozing_notDozing() { + // GIVEN state for "heads up when dozing" is true ensureStateForHeadsUpWhenDozing(); + + // WHEN we're not dozing (in ambient display or sleeping) when(mStatusBarStateController.isDozing()).thenReturn(false); + // THEN this entry shouldn't HUN NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } @@ -290,7 +262,7 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { * {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}. */ @Test - public void testShouldHeadsUpWhenDozing_false_suppressingAmbient() { + public void testShouldNotHeadsUpWhenDozing_suppressingAmbient() { ensureStateForHeadsUpWhenDozing(); NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); @@ -301,23 +273,18 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } - /** - * Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't - * get to pulse. - */ @Test - public void testShouldHeadsUpWhenDozing_false_lessImportant() { + public void testShouldNotHeadsUpWhenDozing_lessImportant() { ensureStateForHeadsUpWhenDozing(); + // Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't + // get to pulse NotificationEntry entry = createNotification(IMPORTANCE_LOW); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } - /** - * Heads up can happen. - */ @Test - public void testShouldHeadsUp_true() throws RemoteException { + public void testShouldHeadsUp() throws RemoteException { ensureStateForHeadsUpWhenAwake(); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -325,38 +292,11 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { } /** - * Heads up notifications can be disabled in general. - */ - @Test - public void testShouldHeadsUp_false_noHunsAllowed() throws RemoteException { - ensureStateForHeadsUpWhenAwake(); - - // Set alerts disabled, this should cause heads up to be false - mNotifInterruptionStateProvider.setDisableNotificationAlerts(true); - assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isFalse(); - - NotificationEntry entry = createNotification(IMPORTANCE_HIGH); - assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); - } - - /** - * If the device is dozing, we don't show as heads up. - */ - @Test - public void testShouldHeadsUp_false_dozing() throws RemoteException { - ensureStateForHeadsUpWhenAwake(); - when(mStatusBarStateController.isDozing()).thenReturn(true); - - NotificationEntry entry = createNotification(IMPORTANCE_HIGH); - assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); - } - - /** * If the notification is a bubble, and the user is not on AOD / lockscreen, then * the bubble is shown rather than the heads up. */ @Test - public void testShouldHeadsUp_false_bubble() throws RemoteException { + public void testShouldNotHeadsUp_bubble() throws RemoteException { ensureStateForHeadsUpWhenAwake(); // Bubble bit only applies to interruption when we're in the shade @@ -369,7 +309,7 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { * If we're not allowed to alert in general, we shouldn't be shown as heads up. */ @Test - public void testShouldHeadsUp_false_alertCommonFalse() throws RemoteException { + public void testShouldNotHeadsUp_filtered() throws RemoteException { ensureStateForHeadsUpWhenAwake(); // Make canAlertCommon false by saying it's filtered out when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true); @@ -383,7 +323,7 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { * {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}. */ @Test - public void testShouldHeadsUp_false_suppressPeek() throws RemoteException { + public void testShouldNotHeadsUp_suppressPeek() throws RemoteException { ensureStateForHeadsUpWhenAwake(); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -399,7 +339,7 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { * to show as a heads up. */ @Test - public void testShouldHeadsUp_false_lessImportant() throws RemoteException { + public void testShouldNotHeadsUp_lessImportant() throws RemoteException { ensureStateForHeadsUpWhenAwake(); NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); @@ -410,7 +350,7 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { * If the device is not in use then we shouldn't be shown as heads up. */ @Test - public void testShouldHeadsUp_false_deviceNotInUse() throws RemoteException { + public void testShouldNotHeadsUp_deviceNotInUse() throws RemoteException { ensureStateForHeadsUpWhenAwake(); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -424,61 +364,58 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } - /** - * If something wants to suppress this heads up, then it shouldn't be shown as a heads up. - */ @Test - public void testShouldHeadsUp_false_suppressed() throws RemoteException { + public void testShouldNotHeadsUp_headsUpSuppressed() throws RemoteException { ensureStateForHeadsUpWhenAwake(); - when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(false); + + // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up. + mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeHeadsUp); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); - verify(mHeadsUpSuppressor).canHeadsUp(any(), any()); } - /** - * On screen alerts don't happen when the device is in VR Mode. - */ @Test - public void testCanAlertAwakeCommon__false_vrMode() { - ensureStateForAlertAwakeCommon(); - when(mPresenter.isDeviceInVrMode()).thenReturn(true); + public void testShouldNotHeadsUpAwake_awakeInterruptsSuppressed() throws RemoteException { + ensureStateForHeadsUpWhenAwake(); - NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); - assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse(); + // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up. + mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeInterruptions); + + NotificationEntry entry = createNotification(IMPORTANCE_HIGH); + assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } /** * On screen alerts don't happen when the notification is snoozed. */ @Test - public void testCanAlertAwakeCommon_false_snoozedPackage() { + public void testShouldNotHeadsUp_snoozedPackage() { + NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); ensureStateForAlertAwakeCommon(); - when(mHeadsUpManager.isSnoozed(any())).thenReturn(true); - NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); - assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse(); + when(mHeadsUpManager.isSnoozed(entry.getSbn().getPackageName())).thenReturn(true); + + assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } - /** - * On screen alerts don't happen when that package has just launched fullscreen. - */ + @Test - public void testCanAlertAwakeCommon_false_justLaunchedFullscreen() { + public void testShouldNotHeadsUp_justLaunchedFullscreen() { ensureStateForAlertAwakeCommon(); + // On screen alerts don't happen when that package has just launched fullscreen. NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); entry.notifyFullScreenIntentLaunched(); - assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse(); + assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } /** * Bubbles can happen. */ @Test - public void testShouldBubbleUp_true() { + public void testShouldBubbleUp() { ensureStateForBubbleUp(); assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isTrue(); } @@ -487,7 +424,7 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { * If the notification doesn't have permission to bubble, it shouldn't bubble. */ @Test - public void shouldBubbleUp_false_notAllowedToBubble() { + public void shouldNotBubbleUp_notAllowedToBubble() { ensureStateForBubbleUp(); NotificationEntry entry = createBubble(); @@ -502,7 +439,7 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { * If the notification isn't a bubble, it should definitely not show as a bubble. */ @Test - public void shouldBubbleUp_false_notABubble() { + public void shouldNotBubbleUp_notABubble() { ensureStateForBubbleUp(); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -517,7 +454,7 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { * If the notification doesn't have bubble metadata, it shouldn't bubble. */ @Test - public void shouldBubbleUp_false_invalidMetadata() { + public void shouldNotBubbleUp_invalidMetadata() { ensureStateForBubbleUp(); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -529,24 +466,18 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse(); } - /** - * If the notification can't heads up in general, it shouldn't bubble. - */ @Test - public void shouldBubbleUp_false_alertAwakeCommonFalse() { + public void shouldNotBubbleUp_suppressedInterruptions() { ensureStateForBubbleUp(); - // Make alert common return false by pretending we're in VR mode - when(mPresenter.isDeviceInVrMode()).thenReturn(true); + // If the notification can't heads up in general, it shouldn't bubble. + mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions); assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse(); } - /** - * If the notification can't heads up in general, it shouldn't bubble. - */ @Test - public void shouldBubbleUp_false_alertCommonFalse() { + public void shouldNotBubbleUp_filteredOut() { ensureStateForBubbleUp(); // Make canAlertCommon false by saying it's filtered out @@ -592,20 +523,45 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { .build(); } - /** - * Testable class overriding constructor. - */ - public static class TestableNotificationInterruptionStateProvider extends - NotificationInterruptionStateProvider { - - TestableNotificationInterruptionStateProvider(Context context, - PowerManager powerManager, IDreamManager dreamManager, - AmbientDisplayConfiguration ambientDisplayConfiguration, - NotificationFilter notificationFilter, - StatusBarStateController statusBarStateController, - BatteryController batteryController) { - super(context, powerManager, dreamManager, ambientDisplayConfiguration, - notificationFilter, batteryController, statusBarStateController); + private final NotificationInterruptSuppressor + mSuppressAwakeHeadsUp = + new NotificationInterruptSuppressor() { + @Override + public String getName() { + return "suppressAwakeHeadsUp"; } - } + + @Override + public boolean suppressAwakeHeadsUp(NotificationEntry entry) { + return true; + } + }; + + private final NotificationInterruptSuppressor + mSuppressAwakeInterruptions = + new NotificationInterruptSuppressor() { + @Override + public String getName() { + return "suppressAwakeInterruptions"; + } + + @Override + public boolean suppressAwakeInterruptions(NotificationEntry entry) { + return true; + } + }; + + private final NotificationInterruptSuppressor + mSuppressInterruptions = + new NotificationInterruptSuppressor() { + @Override + public String getName() { + return "suppressInterruptions"; + } + + @Override + public boolean suppressInterruptions(NotificationEntry entry) { + return true; + } + }; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 5d0349dbbb60..a21a047d9a70 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -56,12 +56,12 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger; import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -108,7 +108,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { @Mock private NotificationEntryListener mEntryListener; @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback; @Mock private HeadsUpManager mHeadsUpManager; - @Mock private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; + @Mock private NotificationInterruptStateProvider mNotificationInterruptionStateProvider; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotificationGutsManager mGutsManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java new file mode 100644 index 000000000000..291c0393a9b9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static com.google.common.truth.Truth.assertThat; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class KeyguardIndicationTextViewTest extends SysuiTestCase { + + private KeyguardIndicationTextView mKeyguardIndicationTextView; + + @Before + public void setup() { + mKeyguardIndicationTextView = new KeyguardIndicationTextView(mContext); + } + + @Test + public void switchIndication_null_hideIndication() { + mKeyguardIndicationTextView.switchIndication(null /* text */); + + assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.INVISIBLE); + assertThat(mKeyguardIndicationTextView.getText()).isEqualTo(""); + } + + @Test + public void switchIndication_emptyText_hideIndication() { + mKeyguardIndicationTextView.switchIndication("" /* text */); + + assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.INVISIBLE); + assertThat(mKeyguardIndicationTextView.getText()).isEqualTo(""); + } + + @Test + public void switchIndication_newText_updateProperly() { + mKeyguardIndicationTextView.switchIndication("test_indication" /* text */); + + assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("test_indication"); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java index c5b69694bdfa..cc2d1c25de38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java @@ -37,7 +37,7 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationShadeWindowBlurController; +import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; @@ -81,7 +81,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private DockManager mDockManager; @Mock private NotificationPanelViewController mNotificationPanelViewController; @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; - @Mock private NotificationShadeWindowBlurController mNotificationShadeWindowBlurController; + @Mock private NotificationShadeDepthController mNotificationShadeDepthController; @Mock private SuperStatusBarViewFactory mStatusBarViewFactory; @Before @@ -116,7 +116,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { new CommandQueue(mContext), mShadeController, mDockManager, - mNotificationShadeWindowBlurController, + mNotificationShadeDepthController, mView, mNotificationPanelViewController, mStatusBarViewFactory); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 1e4df272b02b..b9c5b7c02b1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -67,10 +67,10 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -183,7 +183,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class), mock(NotificationLockscreenUserManager.class), mKeyguardStateController, - mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class), + mock(NotificationInterruptStateProvider.class), mock(MetricsLogger.class), mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor, mActivityIntentHelper, mBubbleController, mShadeController, mFeatureFlags, mNotifPipeline, mNotifCollection) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index b9d2d229cd69..318e9b87fa70 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -16,8 +16,9 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; @@ -35,6 +36,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; +import com.android.systemui.ForegroundServiceNotificationListener; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -48,12 +50,12 @@ import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -62,6 +64,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import java.util.ArrayList; @@ -72,6 +75,9 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { private StatusBarNotificationPresenter mStatusBarNotificationPresenter; + private NotificationInterruptStateProvider mNotificationInterruptStateProvider = + mock(NotificationInterruptStateProvider.class); + private NotificationInterruptSuppressor mInterruptSuppressor; private CommandQueue mCommandQueue; private FakeMetricsLogger mMetricsLogger; private ShadeController mShadeController = mock(ShadeController.class); @@ -95,11 +101,11 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mDependency.injectMockDependency(NotificationViewHierarchyManager.class); mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class); mDependency.injectMockDependency(NotificationLockscreenUserManager.class); - mDependency.injectMockDependency(NotificationInterruptionStateProvider.class); mDependency.injectMockDependency(NotificationMediaManager.class); mDependency.injectMockDependency(VisualStabilityManager.class); mDependency.injectMockDependency(NotificationGutsManager.class); mDependency.injectMockDependency(NotificationShadeWindowController.class); + mDependency.injectMockDependency(ForegroundServiceNotificationListener.class); NotificationEntryManager entryManager = mDependency.injectMockDependency(NotificationEntryManager.class); when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>()); @@ -107,18 +113,25 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { NotificationShadeWindowView notificationShadeWindowView = mock(NotificationShadeWindowView.class); when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources()); + mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext, mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class), notificationShadeWindowView, mock(NotificationListContainerViewGroup.class), mock(DozeScrimController.class), mock(ScrimController.class), mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class), - mock(NotificationAlertingManager.class), mock(KeyguardStateController.class), + mock(KeyguardStateController.class), mock(KeyguardIndicationController.class), mStatusBar, - mock(ShadeControllerImpl.class), mCommandQueue, mInitController); + mock(ShadeControllerImpl.class), mCommandQueue, mInitController, + mNotificationInterruptStateProvider); + mInitController.executePostInitTasks(); + ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor = + ArgumentCaptor.forClass(NotificationInterruptSuppressor.class); + verify(mNotificationInterruptStateProvider).addSuppressor(suppressorCaptor.capture()); + mInterruptSuppressor = suppressorCaptor.getValue(); } @Test - public void testHeadsUp_disabledStatusBar() { + public void testSuppressHeadsUp_disabledStatusBar() { Notification n = new Notification.Builder(getContext(), "a").build(); NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") @@ -130,12 +143,12 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { false /* animate */); TestableLooper.get(this).processAllMessages(); - assertFalse("The panel shouldn't allow heads up while disabled", - mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn())); + assertTrue("The panel should suppress heads up while disabled", + mInterruptSuppressor.suppressAwakeHeadsUp(entry)); } @Test - public void testHeadsUp_disabledNotificationShade() { + public void testSuppressHeadsUp_disabledNotificationShade() { Notification n = new Notification.Builder(getContext(), "a").build(); NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") @@ -147,8 +160,39 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { false /* animate */); TestableLooper.get(this).processAllMessages(); - assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled", - mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn())); + assertTrue("The panel should suppress interruptions while notification shade " + + "disabled", + mInterruptSuppressor.suppressAwakeHeadsUp(entry)); + } + + @Test + public void testSuppressInterruptions_vrMode() { + Notification n = new Notification.Builder(getContext(), "a").build(); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("a") + .setOpPkg("a") + .setTag("a") + .setNotification(n) + .build(); + mStatusBarNotificationPresenter.mVrMode = true; + + assertTrue("Vr mode should suppress interruptions", + mInterruptSuppressor.suppressAwakeInterruptions(entry)); + } + + @Test + public void testSuppressInterruptions_statusBarAlertsDisabled() { + Notification n = new Notification.Builder(getContext(), "a").build(); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("a") + .setOpPkg("a") + .setTag("a") + .setNotification(n) + .build(); + when(mStatusBar.areNotificationAlertsDisabled()).thenReturn(true); + + assertTrue("StatusBar alerts disabled shouldn't allow interruptions", + mInterruptSuppressor.suppressInterruptions(entry)); } @Test @@ -172,4 +216,3 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } } } - diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index e4079275ddfc..679ac2224128 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -42,7 +42,7 @@ import android.app.Notification; import android.app.StatusBarManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; -import android.content.Context; +import android.content.ContentResolver; import android.content.IntentFilter; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.fingerprint.FingerprintManager; @@ -109,18 +109,18 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.init.NotificationsController; +import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; +import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerFake; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -130,6 +130,7 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; @@ -161,7 +162,7 @@ public class StatusBarTest extends SysuiTestCase { private StatusBar mStatusBar; private FakeMetricsLogger mMetricsLogger; private PowerManager mPowerManager; - private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider; + private TestableNotificationInterruptStateProviderImpl mNotificationInterruptStateProvider; @Mock private NotificationsController mNotificationsController; @Mock private LightBarController mLightBarController; @@ -179,7 +180,6 @@ public class StatusBarTest extends SysuiTestCase { @Mock private DozeScrimController mDozeScrimController; @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; @Mock private BiometricUnlockController mBiometricUnlockController; - @Mock private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor; @Mock private VisualStabilityManager mVisualStabilityManager; @Mock private NotificationListener mNotificationListener; @Mock private KeyguardViewMediator mKeyguardViewMediator; @@ -193,9 +193,9 @@ public class StatusBarTest extends SysuiTestCase { @Mock private NotificationEntryListener mEntryListener; @Mock private NotificationFilter mNotificationFilter; @Mock private NotificationAlertingManager mNotificationAlertingManager; + @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration; @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration; @Mock private NotificationShadeWindowView mNotificationShadeWindowView; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private AssistManager mAssistManager; @@ -263,10 +263,12 @@ public class StatusBarTest extends SysuiTestCase { mPowerManager = new PowerManager(mContext, powerManagerService, thermalService, Handler.createAsync(Looper.myLooper())); - mNotificationInterruptionStateProvider = - new TestableNotificationInterruptionStateProvider(mContext, mPowerManager, + mNotificationInterruptStateProvider = + new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), + mPowerManager, mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter, - mStatusBarStateController, mBatteryController); + mStatusBarStateController, mBatteryController, mHeadsUpManager, + new Handler(TestableLooper.get(this).getLooper())); mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class)); mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class)); @@ -299,9 +301,6 @@ public class StatusBarTest extends SysuiTestCase { return null; }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any()); - mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter, - mHeadsUpManager, mHeadsUpSuppressor); - when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle(); @@ -348,7 +347,7 @@ public class StatusBarTest extends SysuiTestCase { ), mNotificationGutsManager, notificationLogger, - mNotificationInterruptionStateProvider, + mNotificationInterruptStateProvider, mNotificationViewHierarchyManager, mKeyguardViewMediator, mNotificationAlertingManager, @@ -562,7 +561,6 @@ public class StatusBarTest extends SysuiTestCase { when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); - when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true); Notification n = new Notification.Builder(getContext(), "a") .setGroup("a") @@ -578,7 +576,7 @@ public class StatusBarTest extends SysuiTestCase { .setImportance(IMPORTANCE_HIGH) .build(); - assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); + assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry)); } @Test @@ -587,7 +585,6 @@ public class StatusBarTest extends SysuiTestCase { when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); - when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true); Notification n = new Notification.Builder(getContext(), "a") .setGroup("a") @@ -603,7 +600,7 @@ public class StatusBarTest extends SysuiTestCase { .setImportance(IMPORTANCE_HIGH) .build(); - assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); + assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry)); } @Test @@ -612,7 +609,6 @@ public class StatusBarTest extends SysuiTestCase { when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); - when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true); Notification n = new Notification.Builder(getContext(), "a").build(); @@ -625,7 +621,7 @@ public class StatusBarTest extends SysuiTestCase { .setSuppressedVisualEffects(SUPPRESSED_EFFECT_PEEK) .build(); - assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); + assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry)); } @Test @@ -634,7 +630,6 @@ public class StatusBarTest extends SysuiTestCase { when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); - when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true); Notification n = new Notification.Builder(getContext(), "a").build(); @@ -646,7 +641,7 @@ public class StatusBarTest extends SysuiTestCase { .setImportance(IMPORTANCE_HIGH) .build(); - assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); + assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry)); } @Test @@ -872,19 +867,21 @@ public class StatusBarTest extends SysuiTestCase { verify(mDozeServiceHost).setDozeSuppressed(false); } - public static class TestableNotificationInterruptionStateProvider extends - NotificationInterruptionStateProvider { + public static class TestableNotificationInterruptStateProviderImpl extends + NotificationInterruptStateProviderImpl { - TestableNotificationInterruptionStateProvider( - Context context, + TestableNotificationInterruptStateProviderImpl( + ContentResolver contentResolver, PowerManager powerManager, IDreamManager dreamManager, AmbientDisplayConfiguration ambientDisplayConfiguration, NotificationFilter filter, StatusBarStateController controller, - BatteryController batteryController) { - super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter, - batteryController, controller); + BatteryController batteryController, + HeadsUpManager headsUpManager, + Handler mainHandler) { + super(contentResolver, powerManager, dreamManager, ambientDisplayConfiguration, filter, + batteryController, controller, headsUpManager, mainHandler); mUseHeadsUp = true; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index a0d551c743c4..cddbb9f508c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -207,7 +207,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected void setupNetworkController() { // For now just pretend to be the data sim, so we can test that too. mSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; - when(mMockTm.isDataConnectionEnabled()).thenReturn(true); + when(mMockTm.isDataConnectionAllowed()).thenReturn(true); setDefaultSubId(mSubId); setSubscriptions(mSubId); mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId); @@ -235,7 +235,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { subs.add(subscription); } when(mMockSm.getActiveSubscriptionInfoList()).thenReturn(subs); - when(mMockSm.getActiveAndHiddenSubscriptionInfoList()).thenReturn(subs); + when(mMockSm.getCompleteActiveSubscriptionInfoList()).thenReturn(subs); mNetworkController.doUpdateMobileControllers(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index 3eb0c44491fc..d8b6aac8fc61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -119,7 +119,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testNoInternetIcon_withDefaultSub() { setupNetworkController(); - when(mMockTm.isDataConnectionEnabled()).thenReturn(false); + when(mMockTm.isDataConnectionAllowed()).thenReturn(false); setupDefaultSignal(); updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0); setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false); @@ -133,7 +133,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testDataDisabledIcon_withDefaultSub() { setupNetworkController(); - when(mMockTm.isDataConnectionEnabled()).thenReturn(false); + when(mMockTm.isDataConnectionAllowed()).thenReturn(false); setupDefaultSignal(); updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0); setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false); @@ -147,7 +147,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testNonDefaultSIM_showsFullSignal_connected() { setupNetworkController(); - when(mMockTm.isDataConnectionEnabled()).thenReturn(false); + when(mMockTm.isDataConnectionAllowed()).thenReturn(false); setupDefaultSignal(); setDefaultSubId(mSubId + 1); updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0); @@ -162,7 +162,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testNonDefaultSIM_showsFullSignal_disconnected() { setupNetworkController(); - when(mMockTm.isDataConnectionEnabled()).thenReturn(false); + when(mMockTm.isDataConnectionAllowed()).thenReturn(false); setupDefaultSignal(); setDefaultSubId(mSubId + 1); updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0); @@ -177,7 +177,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testDataDisabledIcon_UserNotSetup() { setupNetworkController(); - when(mMockTm.isDataConnectionEnabled()).thenReturn(false); + when(mMockTm.isDataConnectionAllowed()).thenReturn(false); setupDefaultSignal(); updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0); setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false); @@ -192,7 +192,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testAlwaysShowDataRatIcon() { setupDefaultSignal(); - when(mMockTm.isDataConnectionEnabled()).thenReturn(false); + when(mMockTm.isDataConnectionAllowed()).thenReturn(false); updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, TelephonyManager.NETWORK_TYPE_GSM); diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index 5b73dd53a285..2fbba68f1e03 100644 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ b/packages/Tethering/common/TetheringLib/Android.bp @@ -62,26 +62,14 @@ java_library { apex_available: ["com.android.tethering"], } -droidstubs { - name: "framework-tethering-stubs-sources", - defaults: ["framework-module-stubs-defaults-module_libs_api"], +stubs_defaults { + name: "framework-tethering-stubs-defaults", srcs: [ "src/android/net/TetheredClient.java", "src/android/net/TetheringManager.java", "src/android/net/TetheringConstants.java", ], - libs: [ - "tethering-aidl-interfaces-java", - "framework-all", - ], - sdk_version: "core_platform", -} - -java_library { - name: "framework-tethering-stubs", - srcs: [":framework-tethering-stubs-sources"], - libs: ["framework-all"], - sdk_version: "core_platform", + libs: ["tethering-aidl-interfaces-java"], } filegroup { @@ -101,3 +89,53 @@ filegroup { ], path: "src" } + +droidstubs { + name: "framework-tethering-stubs-srcs-publicapi", + defaults: [ + "framework-module-stubs-defaults-publicapi", + "framework-tethering-stubs-defaults", + ], +} + +droidstubs { + name: "framework-tethering-stubs-srcs-systemapi", + defaults: [ + "framework-module-stubs-defaults-systemapi", + "framework-tethering-stubs-defaults", + ], +} + +droidstubs { + name: "framework-tethering-api-module_libs_api", + defaults: [ + "framework-module-api-defaults-module_libs_api", + "framework-tethering-stubs-defaults", + ], +} + +droidstubs { + name: "framework-tethering-stubs-srcs-module_libs_api", + defaults: [ + "framework-module-stubs-defaults-module_libs_api", + "framework-tethering-stubs-defaults", + ], +} + +java_library { + name: "framework-tethering-stubs-publicapi", + srcs: [":framework-tethering-stubs-srcs-publicapi"], + sdk_version: "current", +} + +java_library { + name: "framework-tethering-stubs-systemapi", + srcs: [":framework-tethering-stubs-srcs-systemapi"], + sdk_version: "system_current", +} + +java_library { + name: "framework-tethering-stubs-module_libs_api", + srcs: [":framework-tethering-stubs-srcs-module_libs_api"], + sdk_version: "module_current", +} diff --git a/services/Android.bp b/services/Android.bp index ef47867eed48..c4be0032ade8 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -78,7 +78,7 @@ java_library { libs: [ "android.hidl.manager-V1.0-java", - "framework-tethering-stubs", + "framework-tethering-stubs-module_libs_api", ], plugins: [ diff --git a/services/api/current.txt b/services/api/current.txt index 8c90165eeb4a..9bbb3efcc7c2 100644 --- a/services/api/current.txt +++ b/services/api/current.txt @@ -3,9 +3,9 @@ package com.android.permission.persistence { public interface RuntimePermissionsPersistence { method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance(); - method public void deleteAsUser(@NonNull android.os.UserHandle); - method @Nullable public com.android.permission.persistence.RuntimePermissionsState readAsUser(@NonNull android.os.UserHandle); - method public void writeAsUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle); + method public void deleteForUser(@NonNull android.os.UserHandle); + method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle); + method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle); } public final class RuntimePermissionsState { @@ -17,7 +17,7 @@ package com.android.permission.persistence { field public static final int NO_VERSION = -1; // 0xffffffff } - public static class RuntimePermissionsState.PermissionState { + public static final class RuntimePermissionsState.PermissionState { ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int); method public int getFlags(); method @NonNull public String getName(); @@ -30,9 +30,9 @@ package com.android.role.persistence { public interface RolesPersistence { method @NonNull public static com.android.role.persistence.RolesPersistence createInstance(); - method public void deleteAsUser(@NonNull android.os.UserHandle); - method @Nullable public com.android.role.persistence.RolesState readAsUser(@NonNull android.os.UserHandle); - method public void writeAsUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle); + method public void deleteForUser(@NonNull android.os.UserHandle); + method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle); + method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle); } public final class RolesState { diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java index 7fe086df0729..3376f2bd74f5 100644 --- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java +++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.os.Bundle; +import android.os.Handler; import android.os.RemoteException; import android.util.Log; import android.util.Slog; @@ -38,11 +39,8 @@ import com.android.server.inputmethod.InputMethodManagerInternal; import java.util.Collections; import java.util.Optional; -import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; /** * Maintains an autofill inline suggestion session that communicates with the IME. @@ -69,7 +67,7 @@ import java.util.concurrent.TimeoutException; final class InlineSuggestionSession { private static final String TAG = "AfInlineSuggestionSession"; - private static final int INLINE_REQUEST_TIMEOUT_MS = 1000; + private static final int INLINE_REQUEST_TIMEOUT_MS = 200; @NonNull private final InputMethodManagerInternal mInputMethodManagerInternal; @@ -80,6 +78,8 @@ final class InlineSuggestionSession { private final Object mLock; @NonNull private final ImeStatusListener mImeStatusListener; + @NonNull + private final Handler mHandler; /** * To avoid the race condition, one should not access {@code mPendingImeResponse} without @@ -105,10 +105,11 @@ final class InlineSuggestionSession { private boolean mImeInputViewStarted = false; InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal, - int userId, ComponentName componentName) { + int userId, ComponentName componentName, Handler handler) { mInputMethodManagerInternal = inputMethodManagerInternal; mUserId = userId; mComponentName = componentName; + mHandler = handler; mLock = new Object(); mImeStatusListener = new ImeStatusListener() { @Override @@ -137,7 +138,8 @@ final class InlineSuggestionSession { }; } - public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId) { + public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId, + @NonNull Consumer<InlineSuggestionsRequest> requestConsumer) { if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequest called for " + autofillId); synchronized (mLock) { @@ -154,26 +156,16 @@ final class InlineSuggestionSession { mUserId, new InlineSuggestionsRequestInfo(mComponentName, autofillId, new Bundle()), new InlineSuggestionsRequestCallbackImpl(mPendingImeResponse, - mImeStatusListener)); + mImeStatusListener, requestConsumer, mHandler, mLock)); } } - public Optional<InlineSuggestionsRequest> waitAndGetInlineSuggestionsRequest() { + public Optional<InlineSuggestionsRequest> getInlineSuggestionsRequest() { final CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse(); - if (pendingImeResponse == null) { + if (pendingImeResponse == null || !pendingImeResponse.isDone()) { return Optional.empty(); } - try { - return Optional.ofNullable(pendingImeResponse.get(INLINE_REQUEST_TIMEOUT_MS, - TimeUnit.MILLISECONDS)).map(ImeResponse::getRequest); - } catch (TimeoutException e) { - Log.w(TAG, "Exception getting inline suggestions request in time: " + e); - } catch (CancellationException e) { - Log.w(TAG, "Inline suggestions request cancelled"); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - return Optional.empty(); + return Optional.ofNullable(pendingImeResponse.getNow(null)).map(ImeResponse::getRequest); } public boolean hideInlineSuggestionsUi(@NonNull AutofillId autofillId) { @@ -200,8 +192,7 @@ final class InlineSuggestionSession { if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked without IMS request"); return false; } - // There is no need to wait on the CompletableFuture since it should have been completed - // when {@link #waitAndGetInlineSuggestionsRequest()} was called. + // There is no need to wait on the CompletableFuture since it should have been completed. ImeResponse imeResponse = completedImsResponse.getNow(null); if (imeResponse == null) { if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked with pending IMS response"); @@ -249,20 +240,50 @@ final class InlineSuggestionSession { private static final class InlineSuggestionsRequestCallbackImpl extends IInlineSuggestionsRequestCallback.Stub { + private final Object mLock; + @GuardedBy("mLock") private final CompletableFuture<ImeResponse> mResponse; + @GuardedBy("mLock") + private final Consumer<InlineSuggestionsRequest> mRequestConsumer; private final ImeStatusListener mImeStatusListener; + private final Handler mHandler; + private final Runnable mTimeoutCallback; private InlineSuggestionsRequestCallbackImpl(CompletableFuture<ImeResponse> response, - ImeStatusListener imeStatusListener) { + ImeStatusListener imeStatusListener, + Consumer<InlineSuggestionsRequest> requestConsumer, + Handler handler, Object lock) { mResponse = response; mImeStatusListener = imeStatusListener; + mRequestConsumer = requestConsumer; + mLock = lock; + + mHandler = handler; + mTimeoutCallback = () -> { + Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest."); + synchronized (mLock) { + completeIfNotLocked(null); + } + }; + mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS); + } + + private void completeIfNotLocked(@Nullable ImeResponse response) { + if (mResponse.isDone()) { + return; + } + mResponse.complete(response); + mRequestConsumer.accept(response == null ? null : response.mRequest); + mHandler.removeCallbacks(mTimeoutCallback); } @BinderThread @Override public void onInlineSuggestionsUnsupported() throws RemoteException { if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called."); - mResponse.complete(null); + synchronized (mLock) { + completeIfNotLocked(null); + } } @BinderThread @@ -281,9 +302,13 @@ final class InlineSuggestionSession { mImeStatusListener.onInputMethodFinishInputView(imeFieldId); } if (request != null && callback != null) { - mResponse.complete(new ImeResponse(request, callback)); + synchronized (mLock) { + completeIfNotLocked(new ImeResponse(request, callback)); + } } else { - mResponse.complete(null); + synchronized (mLock) { + completeIfNotLocked(null); + } } } diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index e73f9ce4f7c0..53afa6e283d9 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -57,6 +57,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.IResultReceiver; import com.android.server.autofill.ui.InlineSuggestionFactory; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; @@ -255,8 +256,12 @@ final class RemoteAugmentedAutofillService mCallbacks.logAugmentedAutofillSelected(sessionId, dataset.getId()); try { - client.autofill(sessionId, dataset.getFieldIds(), - dataset.getFieldValues()); + final ArrayList<AutofillId> fieldIds = dataset.getFieldIds(); + final int size = fieldIds.size(); + final boolean hideHighlight = size == 1 + && fieldIds.get(0).equals(focusedId); + client.autofill(sessionId, fieldIds, dataset.getFieldValues(), + hideHighlight); } catch (RemoteException e) { Slog.w(TAG, "Encounter exception autofilling the values"); } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 538082d2f67e..5064663871e3 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -18,6 +18,7 @@ package com.android.server.autofill; import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; +import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED; import static android.view.autofill.AutofillManager.ACTION_START_SESSION; @@ -113,7 +114,9 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; /** * A session for a given activity. @@ -306,7 +309,47 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Receiver of assist data from the app's {@link Activity}. */ - private final IAssistDataReceiver mAssistReceiver = new IAssistDataReceiver.Stub() { + private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl(); + + private final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub { + + @GuardedBy("mLock") + private InlineSuggestionsRequest mPendingInlineSuggestionsRequest; + @GuardedBy("mLock") + private FillRequest mPendingFillRequest; + @GuardedBy("mLock") + private CountDownLatch mCountDownLatch; + + @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked( + boolean isInlineRequest) { + mCountDownLatch = new CountDownLatch(isInlineRequest ? 2 : 1); + mPendingFillRequest = null; + mPendingInlineSuggestionsRequest = null; + return isInlineRequest ? (inlineSuggestionsRequest) -> { + synchronized (mLock) { + mPendingInlineSuggestionsRequest = inlineSuggestionsRequest; + mCountDownLatch.countDown(); + maybeRequestFillLocked(); + } + } : null; + } + + void maybeRequestFillLocked() { + if (mCountDownLatch == null || mCountDownLatch.getCount() > 0 + || mPendingFillRequest == null) { + return; + } + if (mPendingInlineSuggestionsRequest != null) { + mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), + mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(), + mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest); + } + mRemoteFillService.onFillRequest(mPendingFillRequest); + mPendingInlineSuggestionsRequest = null; + mPendingFillRequest = null; + mCountDownLatch = null; + } + @Override public void onHandleAssistData(Bundle resultData) throws RemoteException { if (mRemoteFillService == null) { @@ -401,17 +444,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<FillContext> contexts = mergePreviousSessionLocked(/* forSave= */ false); - - final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest = - mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest(); request = new FillRequest(requestId, contexts, mClientState, flags, - inlineSuggestionsRequest.orElse(null)); + /*inlineSuggestionsRequest=*/null); + + mPendingFillRequest = request; + mCountDownLatch.countDown(); + maybeRequestFillLocked(); } if (mActivityToken != null) { mService.sendActivityAssistDataToContentCapture(mActivityToken, resultData); } - mRemoteFillService.onFillRequest(request); } @Override @@ -604,9 +647,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState, int newState, int flags) { if (isInlineSuggestionsEnabledLocked()) { - mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId); + Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = + mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true); + if (inlineSuggestionsRequestConsumer != null) { + mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId, + inlineSuggestionsRequestConsumer); + } + } else { + mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false); } - requestNewFillResponseLocked(viewState, newState, flags); } @@ -624,7 +673,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + ", flags=" + flags + ")"); } mForAugmentedAutofillOnly = true; - triggerAugmentedAutofillLocked(); + triggerAugmentedAutofillLocked(flags); return; } @@ -707,7 +756,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState setClientLocked(client); mInlineSuggestionSession = new InlineSuggestionSession(inputMethodManagerInternal, userId, - componentName); + componentName, handler); mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED) .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags)); @@ -834,7 +883,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Although "standard" autofill is disabled, it might still trigger augmented autofill - if (triggerAugmentedAutofillLocked() != null) { + if (triggerAugmentedAutofillLocked(requestFlags) != null) { mForAugmentedAutofillOnly = true; if (sDebug) { Slog.d(TAG, "Service disabled autofill for " + mComponentName @@ -2465,7 +2514,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // triggered augmented autofill if (!isSameViewEntered) { if (sDebug) Slog.d(TAG, "trigger augmented autofill."); - triggerAugmentedAutofillLocked(); + triggerAugmentedAutofillLocked(flags); } else { if (sDebug) Slog.d(TAG, "skip augmented autofill for same view."); } @@ -2662,7 +2711,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response, @Nullable String filterText) { final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest = - mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest(); + mInlineSuggestionSession.getInlineSuggestionsRequest(); if (!inlineSuggestionsRequest.isPresent()) { Log.w(TAG, "InlineSuggestionsRequest unavailable"); return false; @@ -2863,8 +2912,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // The default autofill service cannot fullfill the request, let's check if the augmented // autofill service can. - mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(); - if (mAugmentedAutofillDestroyer == null) { + mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags); + if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) { if (sVerbose) { Slog.v(TAG, "canceling session " + id + " when service returned null and it cannot " + "be augmented. AutofillableIds: " + autofillableIds); @@ -2874,8 +2923,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState removeSelf(); } else { if (sVerbose) { - Slog.v(TAG, "keeping session " + id + " when service returned null but " - + "it can be augmented. AutofillableIds: " + autofillableIds); + if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) { + Slog.v(TAG, "keeping session " + id + " when service returned null and " + + "augmented service is disabled for password fields. " + + "AutofillableIds: " + autofillableIds); + } else { + Slog.v(TAG, "keeping session " + id + " when service returned null but " + + "it can be augmented. AutofillableIds: " + autofillableIds); + } } mAugmentedAutofillableIds = autofillableIds; try { @@ -2889,12 +2944,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Tries to trigger Augmented Autofill when the standard service could not fulfill a request. * + * <p> The request may not have been sent when this method returns as it may be waiting for + * the inline suggestion request asynchronously. + * * @return callback to destroy the autofill UI, or {@code null} if not supported. */ // TODO(b/123099468): might need to call it in other places, like when the service returns a // non-null response but without datasets (for example, just SaveInfo) @GuardedBy("mLock") - private Runnable triggerAugmentedAutofillLocked() { + private Runnable triggerAugmentedAutofillLocked(int flags) { + // (TODO: b/141703197) Fix later by passing info to service. + if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) { + return null; + } + // Check if Smart Suggestions is supported... final @SmartSuggestionMode int supportedModes = mService .getSupportedSmartSuggestionModesLocked(); @@ -2966,6 +3029,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId); + final Consumer<InlineSuggestionsRequest> requestAugmentedAutofill = + (inlineSuggestionsRequest) -> { + remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, + focusedId, + currentValue, inlineSuggestionsRequest, + /*inlineSuggestionsCallback=*/ + response -> mInlineSuggestionSession.onInlineSuggestionsResponse( + mCurrentViewId, response), + /*onErrorCallback=*/ () -> { + synchronized (mLock) { + cancelAugmentedAutofillLocked(); + } + }, mService.getRemoteInlineSuggestionRenderServiceLocked()); + }; + // There are 3 cases when augmented autofill should ask IME for a new request: // 1. standard autofill provider is None // 2. standard autofill provider doesn't support inline (and returns null response) @@ -2973,21 +3051,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // doesn't want autofill if (mForAugmentedAutofillOnly || !isInlineSuggestionsEnabledLocked()) { if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill"); - mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId); - } - - Optional<InlineSuggestionsRequest> inlineSuggestionsRequest = - mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest(); - remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId, - currentValue, inlineSuggestionsRequest.orElse(null), - response -> mInlineSuggestionSession.onInlineSuggestionsResponse( - mCurrentViewId, response), - () -> { - synchronized (mLock) { - cancelAugmentedAutofillLocked(); - } - }, mService.getRemoteInlineSuggestionRenderServiceLocked()); - + mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId, + /*requestConsumer=*/ requestAugmentedAutofill); + } else { + requestAugmentedAutofill.accept( + mInlineSuggestionSession.getInlineSuggestionsRequest().orElse(null)); + } if (mAugmentedAutofillDestroyer == null) { mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest(); } @@ -3370,6 +3439,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final List<AutofillId> ids = new ArrayList<>(entryCount); final List<AutofillValue> values = new ArrayList<>(entryCount); boolean waitingDatasetAuth = false; + boolean hideHighlight = (entryCount == 1 + && dataset.getFieldIds().get(0).equals(mCurrentViewId)); for (int i = 0; i < entryCount; i++) { if (dataset.getFieldValues().get(i) == null) { continue; @@ -3393,7 +3464,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (sDebug) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset); - mClient.autofill(id, ids, values); + mClient.autofill(id, ids, values, hideHighlight); if (dataset.getId() != null) { if (mSelectedDatasetIds == null) { mSelectedDatasetIds = new ArrayList<>(); diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java index ee59d89b7f15..0ca9dd92877f 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -329,8 +329,14 @@ public final class InlineSuggestionFactory { @NonNull Runnable onErrorCallback) { return new IInlineSuggestionUiCallback.Stub() { @Override - public void onAutofill() throws RemoteException { + public void onClick() throws RemoteException { onAutofillCallback.run(); + callback.onClick(); + } + + @Override + public void onLongClick() throws RemoteException { + callback.onLongClick(); } @Override diff --git a/services/core/Android.bp b/services/core/Android.bp index 4cc65900d099..942d5633e381 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -99,7 +99,7 @@ java_library_static { "android.hardware.tv.cec-V1.0-java", "android.hardware.vibrator-java", "app-compat-annotations", - "framework-tethering-stubs", + "framework-tethering-stubs-module_libs_api", "ike-stubs", ], diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 7840b1957031..9b04e7931f7c 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1672,7 +1672,7 @@ class AlarmManagerService extends SystemService { | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); - intent.putExtra("time-zone", zone.getID()); + intent.putExtra(Intent.EXTRA_TIMEZONE, zone.getID()); getContext().sendBroadcastAsUser(intent, UserHandle.ALL); } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 7287a44600fa..ecf1f134a3de 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -7803,12 +7803,15 @@ public class ConnectivityService extends IConnectivityManager.Stub private void handleNetworkTestedWithExtras( @NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) { final NetworkAgentInfo nai = reportEvent.mNai; + final NetworkCapabilities networkCapabilities = + new NetworkCapabilities(nai.networkCapabilities); + clearNetworkCapabilitiesUids(networkCapabilities); final ConnectivityReport report = new ConnectivityReport( reportEvent.mNai.network, reportEvent.mTimestampMillis, nai.linkProperties, - nai.networkCapabilities, + networkCapabilities, extras); final List<IConnectivityDiagnosticsCallback> results = getMatchingPermissionedCallbacks(nai); @@ -7824,13 +7827,16 @@ public class ConnectivityService extends IConnectivityManager.Stub private void handleDataStallSuspected( @NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod, @NonNull PersistableBundle extras) { + final NetworkCapabilities networkCapabilities = + new NetworkCapabilities(nai.networkCapabilities); + clearNetworkCapabilitiesUids(networkCapabilities); final DataStallReport report = new DataStallReport( nai.network, timestampMillis, detectionMethod, nai.linkProperties, - nai.networkCapabilities, + networkCapabilities, extras); final List<IConnectivityDiagnosticsCallback> results = getMatchingPermissionedCallbacks(nai); @@ -7856,6 +7862,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void clearNetworkCapabilitiesUids(@NonNull NetworkCapabilities nc) { + nc.setUids(null); + nc.setAdministratorUids(Collections.EMPTY_LIST); + nc.setOwnerUid(Process.INVALID_UID); + } + private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks( @NonNull NetworkAgentInfo nai) { final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>(); diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index acd40395f611..d814b9c9f1fa 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -200,7 +200,7 @@ public class LocationManagerService extends ILocationManager.Stub { // time private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100; - private static final String FEATURE_ID = "LocationService"; + private static final String ATTRIBUTION_TAG = "LocationService"; private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest(); @@ -246,7 +246,7 @@ public class LocationManagerService extends ILocationManager.Stub { private int mBatterySaverMode; private LocationManagerService(Context context) { - mContext = context.createFeatureContext(FEATURE_ID); + mContext = context.createAttributionContext(ATTRIBUTION_TAG); mHandler = FgThread.getHandler(); mLocalService = new LocalService(); diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 2cfe404f8c6f..b464422e9e3d 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -161,9 +161,6 @@ public class PackageWatchdog { private final Runnable mSaveToFile = this::saveToFile; private final SystemClock mSystemClock; private final BootThreshold mBootThreshold; - // The set of packages that have been synced with the ExplicitHealthCheckController - @GuardedBy("mLock") - private Set<String> mRequestedHealthCheckPackages = new ArraySet<>(); @GuardedBy("mLock") private boolean mIsPackagesReady; // Flag to control whether explicit health checks are supported or not @@ -627,22 +624,17 @@ public class PackageWatchdog { * @see #syncRequestsAsync */ private void syncRequests() { - boolean syncRequired = false; + Set<String> packages = null; synchronized (mLock) { if (mIsPackagesReady) { - Set<String> packages = getPackagesPendingHealthChecksLocked(); - if (!packages.equals(mRequestedHealthCheckPackages)) { - syncRequired = true; - mRequestedHealthCheckPackages = packages; - } + packages = getPackagesPendingHealthChecksLocked(); } // else, we will sync requests when packages become ready } // Call outside lock to avoid holding lock when calling into the controller. - if (syncRequired) { - Slog.i(TAG, "Syncing health check requests for packages: " - + mRequestedHealthCheckPackages); - mHealthCheckController.syncRequests(mRequestedHealthCheckPackages); + if (packages != null) { + Slog.i(TAG, "Syncing health check requests for packages: " + packages); + mHealthCheckController.syncRequests(packages); } } diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index 80036bb4b4fa..808d322020cb 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -99,6 +99,8 @@ public class RescueParty { private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue"; private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device"; + private static final String DEVICE_CONFIG_DISABLE_FLAG = "disable_rescue_party"; + private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; @@ -114,6 +116,14 @@ public class RescueParty { return false; } + // We're disabled if the DeviceConfig disable flag is set to true. + // This is in case that an emergency rollback of the feature is needed. + if (DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONFIGURATION, DEVICE_CONFIG_DISABLE_FLAG, false)) { + Slog.v(TAG, "Disabled because of DeviceConfig flag"); + return true; + } + // We're disabled on all engineering devices if (Build.IS_ENG) { Slog.v(TAG, "Disabled because of eng build"); diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java index 9082dca1022c..db3db0c80e3c 100644 --- a/services/core/java/com/android/server/SensorNotificationService.java +++ b/services/core/java/com/android/server/SensorNotificationService.java @@ -48,7 +48,7 @@ public class SensorNotificationService extends SystemService private static final long MILLIS_2010_1_1 = 1262358000000l; - private static final String FEATURE_ID = "SensorNotificationService"; + private static final String ATTRIBUTION_TAG = "SensorNotificationService"; private Context mContext; private SensorManager mSensorManager; @@ -59,7 +59,7 @@ public class SensorNotificationService extends SystemService private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME; public SensorNotificationService(Context context) { - super(context.createFeatureContext(FEATURE_ID)); + super(context.createAttributionContext(ATTRIBUTION_TAG)); mContext = getContext(); } diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java index b43ae36c7ef5..cfb79aa3a210 100644 --- a/services/core/java/com/android/server/ServiceWatcher.java +++ b/services/core/java/com/android/server/ServiceWatcher.java @@ -100,7 +100,7 @@ public class ServiceWatcher implements ServiceConnection { @Nullable public final ComponentName component; @UserIdInt public final int userId; - private ServiceInfo(ResolveInfo resolveInfo, int currentUserId) { + ServiceInfo(ResolveInfo resolveInfo, int currentUserId) { Preconditions.checkArgument(resolveInfo.serviceInfo.getComponentName() != null); Bundle metadata = resolveInfo.serviceInfo.metaData; @@ -316,6 +316,7 @@ public class ServiceWatcher implements ServiceConnection { } mContext.unbindService(this); + onServiceDisconnected(mServiceInfo.component); mServiceInfo = ServiceInfo.NONE; } @@ -339,15 +340,13 @@ public class ServiceWatcher implements ServiceConnection { @Override public final void onServiceConnected(ComponentName component, IBinder binder) { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + Preconditions.checkState(mBinder == null); if (D) { Log.i(TAG, getLogPrefix() + " connected to " + component.toShortString()); } mBinder = binder; - - // we always run the on bind callback even if we know that the binder is dead already so - // that there are always balance pairs of bind/unbind callbacks if (mOnBind != null) { try { mOnBind.run(binder); @@ -357,19 +356,16 @@ public class ServiceWatcher implements ServiceConnection { Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e); } } - - try { - // setting the binder to null lets us skip queued transactions - binder.linkToDeath(() -> mBinder = null, 0); - } catch (RemoteException e) { - mBinder = null; - } } @Override public final void onServiceDisconnected(ComponentName component) { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + if (mBinder == null) { + return; + } + if (D) { Log.i(TAG, getLogPrefix() + " disconnected from " + component.toShortString()); } @@ -391,18 +387,18 @@ public class ServiceWatcher implements ServiceConnection { onBestServiceChanged(true); } - private void onUserSwitched(@UserIdInt int userId) { + void onUserSwitched(@UserIdInt int userId) { mCurrentUserId = userId; onBestServiceChanged(false); } - private void onUserUnlocked(@UserIdInt int userId) { + void onUserUnlocked(@UserIdInt int userId) { if (userId == mCurrentUserId) { onBestServiceChanged(false); } } - private void onPackageChanged(String packageName) { + void onPackageChanged(String packageName) { // force a rebind if the changed package was the currently connected package String currentPackageName = mServiceInfo.component != null ? mServiceInfo.component.getPackageName() : null; diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 7c833faee1bd..12a1a9516329 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -75,7 +75,9 @@ import java.time.DateTimeException; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -293,7 +295,7 @@ final class UiModeManagerService extends SystemService { public void onChange(boolean selfChange, Uri uri) { synchronized (mLock) { // setup wizard is done now so we can unblock - if (setupWizardCompleteForCurrentUser()) { + if (setupWizardCompleteForCurrentUser() && !selfChange) { mSetupWizardComplete = true; getContext().getContentResolver() .unregisterContentObserver(mSetupWizardObserver); @@ -348,6 +350,9 @@ final class UiModeManagerService extends SystemService { IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); context.registerReceiver(mBatteryReceiver, batteryFilter); + context.registerReceiver(mSettingsRestored, + new IntentFilter(Intent.ACTION_SETTING_RESTORED), null, mHandler); + mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class); initPowerSave(); @@ -395,6 +400,22 @@ final class UiModeManagerService extends SystemService { mHandler.post(() -> updateSystemProperties()); } + private final BroadcastReceiver mSettingsRestored = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + List<String> settings = Arrays.asList( + Secure.UI_NIGHT_MODE, Secure.DARK_THEME_CUSTOM_START_TIME, + Secure.DARK_THEME_CUSTOM_END_TIME); + if (settings.contains(intent.getExtras().getCharSequence(Intent.EXTRA_SETTING_NAME))) { + synchronized (mLock) { + updateNightModeFromSettingsLocked(context, context.getResources(), + UserHandle.getCallingUserId()); + updateConfigurationLocked(); + } + } + } + }; + private void initPowerSave() { mPowerSave = mLocalPowerManager.getLowPowerState(ServiceType.NIGHT_MODE) @@ -1297,9 +1318,9 @@ final class UiModeManagerService extends SystemService { if (Sandman.shouldStartDockApp(getContext(), homeIntent)) { try { int result = ActivityTaskManager.getService().startActivityWithConfig( - null, getContext().getBasePackageName(), getContext().getFeatureId(), - homeIntent, null, null, null, 0, 0, mConfiguration, null, - UserHandle.USER_CURRENT); + null, getContext().getBasePackageName(), + getContext().getAttributionTag(), homeIntent, null, null, null, 0, 0, + mConfiguration, null, UserHandle.USER_CURRENT); if (ActivityManager.isStartResultSuccessful(result)) { dockAppStarted = true; } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 59f64ac8c689..8f5fbf7431e1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2931,25 +2931,35 @@ final class ActivityManagerShellCommand extends ShellCommand { final PlatformCompat platformCompat = (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); String toggleValue = getNextArgRequired(); - if (toggleValue.equals("reset-all")) { - final String packageName = getNextArgRequired(); - pw.println("Reset all changes for " + packageName + " to default value."); - platformCompat.clearOverrides(packageName); - return 0; - } - long changeId; - String changeIdString = getNextArgRequired(); - try { - changeId = Long.parseLong(changeIdString); - } catch (NumberFormatException e) { - changeId = platformCompat.lookupChangeId(changeIdString); - } - if (changeId == -1) { - pw.println("Unknown or invalid change: '" + changeIdString + "'."); - return -1; + boolean toggleAll = false; + int targetSdkVersion = -1; + long changeId = -1; + + if (toggleValue.endsWith("-all")) { + toggleValue = toggleValue.substring(0, toggleValue.lastIndexOf("-all")); + toggleAll = true; + if (!toggleValue.equals("reset")) { + try { + targetSdkVersion = Integer.parseInt(getNextArgRequired()); + } catch (NumberFormatException e) { + pw.println("Invalid targetSdkVersion!"); + return -1; + } + } + } else { + String changeIdString = getNextArgRequired(); + try { + changeId = Long.parseLong(changeIdString); + } catch (NumberFormatException e) { + changeId = platformCompat.lookupChangeId(changeIdString); + } + if (changeId == -1) { + pw.println("Unknown or invalid change: '" + changeIdString + "'."); + return -1; + } } String packageName = getNextArgRequired(); - if (!platformCompat.isKnownChangeId(changeId)) { + if (!toggleAll && !platformCompat.isKnownChangeId(changeId)) { pw.println("Warning! Change " + changeId + " is not known yet. Enabling/disabling it" + " could have no effect."); } @@ -2958,22 +2968,49 @@ final class ActivityManagerShellCommand extends ShellCommand { try { switch (toggleValue) { case "enable": - enabled.add(changeId); - CompatibilityChangeConfig overrides = - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); - pw.println("Enabled change " + changeId + " for " + packageName + "."); + if (toggleAll) { + int numChanges = platformCompat.enableTargetSdkChanges(packageName, + targetSdkVersion); + if (numChanges == 0) { + pw.println("No changes were enabled."); + return -1; + } + pw.println("Enabled " + numChanges + " changes gated by targetSdkVersion " + + targetSdkVersion + " for " + packageName + "."); + } else { + enabled.add(changeId); + CompatibilityChangeConfig overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); + pw.println("Enabled change " + changeId + " for " + packageName + "."); + } return 0; case "disable": - disabled.add(changeId); - overrides = - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); - pw.println("Disabled change " + changeId + " for " + packageName + "."); + if (toggleAll) { + int numChanges = platformCompat.disableTargetSdkChanges(packageName, + targetSdkVersion); + if (numChanges == 0) { + pw.println("No changes were disabled."); + return -1; + } + pw.println("Disabled " + numChanges + " changes gated by targetSdkVersion " + + targetSdkVersion + " for " + packageName + "."); + } else { + disabled.add(changeId); + CompatibilityChangeConfig overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); + pw.println("Disabled change " + changeId + " for " + packageName + "."); + } return 0; case "reset": + if (toggleAll) { + platformCompat.clearOverrides(packageName); + pw.println("Reset all changes for " + packageName + " to default value."); + return 0; + } if (platformCompat.clearOverride(changeId, packageName)) { pw.println("Reset change " + changeId + " for " + packageName + " to default value."); @@ -3304,6 +3341,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>"); pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>."); pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect)."); + pw.println(" enable-all|disable-all <targetSdkVersion> <PACKAGE_NAME"); + pw.println(" Toggles all changes that are gated by <targetSdkVersion>."); pw.println(" reset-all <PACKAGE_NAME>"); pw.println(" Removes all existing overrides for all changes for "); pw.println(" <PACKAGE_NAME> (back to default behaviour)."); diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 3a6065e18ea5..86d9028f53dc 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -121,6 +121,7 @@ public final class CachedAppOptimizer { static final int COMPACT_PROCESS_MSG = 1; static final int COMPACT_SYSTEM_MSG = 2; static final int SET_FROZEN_PROCESS_MSG = 3; + static final int REPORT_UNFREEZE_MSG = 4; //TODO:change this static definition into a configurable flag. static final int FREEZE_TIMEOUT_MS = 500; @@ -613,30 +614,6 @@ public final class CachedAppOptimizer { FREEZE_TIMEOUT_MS); } - private final class UnfreezeStats { - final int mPid; - final String mName; - final long mFrozenDuration; - - UnfreezeStats(int pid, String name, long frozenDuration) { - mPid = pid; - mName = name; - mFrozenDuration = frozenDuration; - } - - public int getPid() { - return mPid; - } - - public String getName() { - return mName; - } - - public long getFrozenDuration() { - return mFrozenDuration; - } - } - @GuardedBy("mAm") void unfreezeAppLocked(ProcessRecord app) { mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app); @@ -667,12 +644,11 @@ public final class CachedAppOptimizer { Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName); } - UnfreezeStats stats = new UnfreezeStats(app.pid, app.processName, - app.freezeUnfreezeTime - freezeTime); - mFreezeHandler.sendMessage( - mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, REPORT_UNFREEZE, 0, - stats)); + mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG, + app.pid, + (int) Math.min(app.freezeUnfreezeTime - freezeTime, Integer.MAX_VALUE), + app.processName)); } } @@ -945,14 +921,19 @@ public final class CachedAppOptimizer { @Override public void handleMessage(Message msg) { - if (msg.what != SET_FROZEN_PROCESS_MSG) { - return; - } + switch (msg.what) { + case SET_FROZEN_PROCESS_MSG: + freezeProcess((ProcessRecord) msg.obj); + break; + case REPORT_UNFREEZE_MSG: + int pid = msg.arg1; + int frozenDuration = msg.arg2; + String processName = (String) msg.obj; - if (msg.arg1 == DO_FREEZE) { - freezeProcess((ProcessRecord) msg.obj); - } else if (msg.arg1 == REPORT_UNFREEZE) { - reportUnfreeze((UnfreezeStats) msg.obj); + reportUnfreeze(pid, frozenDuration, processName); + break; + default: + return; } } @@ -1015,18 +996,18 @@ public final class CachedAppOptimizer { } } - private void reportUnfreeze(UnfreezeStats stats) { + private void reportUnfreeze(int pid, int frozenDuration, String processName) { - EventLog.writeEvent(EventLogTags.AM_UNFREEZE, stats.getPid(), stats.getName()); + EventLog.writeEvent(EventLogTags.AM_UNFREEZE, pid, processName); // See above for why we're not taking mPhenotypeFlagLock here if (mRandom.nextFloat() < mFreezerStatsdSampleRate) { FrameworkStatsLog.write( FrameworkStatsLog.APP_FREEZE_CHANGED, FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__UNFREEZE_APP, - stats.getPid(), - stats.getName(), - stats.getFrozenDuration()); + pid, + processName, + frozenDuration); } } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 6b165139aefd..0e9970ec7766 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -61,6 +61,7 @@ import android.app.ApplicationExitInfo.SubReason; import android.app.IApplicationThread; import android.app.IUidObserver; import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; import android.compat.annotation.EnabledAfter; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -70,6 +71,7 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManagerInternal; +import android.content.pm.ProcessInfo; import android.content.res.Resources; import android.graphics.Point; import android.net.LocalSocket; @@ -345,6 +347,14 @@ public final class ProcessList { private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id. /** + * Enable sampled memory bug detection in the app. + * @see <a href="https://source.android.com/devices/tech/debug/gwp-asan">GWP-ASan</a>. + */ + @ChangeId + @Disabled + private static final long GWP_ASAN = 135634846; // This is a bug id. + + /** * Apps have no access to the private data directories of any other app, even if the other * app has made them world-readable. */ @@ -1634,6 +1644,28 @@ public final class ProcessList { return gidArray; } + private int decideGwpAsanLevel(ProcessRecord app) { + // Look at the process attribute first. + if (app.processInfo != null && app.processInfo.enableGwpAsan != null) { + return app.processInfo.enableGwpAsan ? Zygote.GWP_ASAN_LEVEL_ALWAYS + : Zygote.GWP_ASAN_LEVEL_NEVER; + } + // Then at the applicaton attribute. + if (app.info.isGwpAsanEnabled() != null) { + return app.info.isGwpAsanEnabled() ? Zygote.GWP_ASAN_LEVEL_ALWAYS + : Zygote.GWP_ASAN_LEVEL_NEVER; + } + // If the app does not specify enableGwpAsan, the default behavior is lottery among the + // system apps, and disabled for user apps, unless overwritten by the compat feature. + if (mPlatformCompat.isChangeEnabled(GWP_ASAN, app.info)) { + return Zygote.GWP_ASAN_LEVEL_ALWAYS; + } + if ((app.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + return Zygote.GWP_ASAN_LEVEL_LOTTERY; + } + return Zygote.GWP_ASAN_LEVEL_NEVER; + } + /** * @return {@code true} if process start is successful, false otherwise. */ @@ -1803,6 +1835,8 @@ public final class ProcessList { runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI; } + runtimeFlags |= decideGwpAsanLevel(app); + String invokeWith = null; if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { // Debuggable apps may include a wrapper script with their library directory. diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index c0298110580e..a78caaced36c 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -384,6 +384,9 @@ class ProcessRecord implements WindowProcessListener { pw.println(processInfo.deniedPermissions.valueAt(i)); } } + if (processInfo.enableGwpAsan != null) { + pw.print(prefix); pw.println(" enableGwpAsan=" + processInfo.enableGwpAsan); + } } pw.print(prefix); pw.print("mRequiredAbi="); pw.print(mRequiredAbi); pw.print(" instructionSet="); pw.println(instructionSet); diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 0a8e70c7d772..bac7565adfa5 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -80,6 +80,7 @@ public class SettingsToPropertiesMapper { @VisibleForTesting static final String[] sDeviceConfigScopes = new String[] { DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, + DeviceConfig.NAMESPACE_CONFIGURATION, DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT, DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS, DeviceConfig.NAMESPACE_MEDIA_NATIVE, diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 7774633fa1be..310664e10bf3 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -19,8 +19,8 @@ package com.android.server.appop; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; -import static android.app.AppOpsManager.CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE; -import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID; +import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP; +import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG; import static android.app.AppOpsManager.FILTER_BY_OP_NAMES; import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME; import static android.app.AppOpsManager.FILTER_BY_UID; @@ -40,6 +40,7 @@ import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OpEventProxyInfo; +import static android.app.AppOpsManager.RestrictionBypass; import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED; import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM; import static android.app.AppOpsManager.UID_STATE_BACKGROUND; @@ -55,6 +56,7 @@ import static android.app.AppOpsManager.extractFlagsFromKey; import static android.app.AppOpsManager.extractUidStateFromKey; import static android.app.AppOpsManager.makeKey; import static android.app.AppOpsManager.modeToName; +import static android.app.AppOpsManager.opAllowSystemBypassRestriction; import static android.app.AppOpsManager.opToName; import static android.app.AppOpsManager.opToPublicName; import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState; @@ -73,33 +75,29 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; -import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AppOpsManager.HistoricalOps; import android.app.AppOpsManager.Mode; import android.app.AppOpsManager.OpEntry; -import android.app.AppOpsManager.OpFeatureEntry; +import android.app.AppOpsManager.AttributedOpEntry; import android.app.AppOpsManager.OpFlags; import android.app.AppOpsManagerInternal; import android.app.AppOpsManagerInternal.CheckOpsDelegate; import android.app.AsyncNotedAppOp; import android.app.RuntimeAppOpAccessMessage; import android.app.SyncNotedAppOp; -import android.compat.Compatibility; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PermissionInfo; import android.content.pm.UserInfo; -import android.content.pm.parsing.component.ParsedFeature; +import android.content.pm.parsing.component.ParsedAttribution; import android.database.ContentObserver; import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION; import android.net.Uri; @@ -373,14 +371,14 @@ public class AppOpsService extends IAppOpsService.Stub { } OpEventProxyInfo acquire(@IntRange(from = 0) int uid, @Nullable String packageName, - @Nullable String featureId) { + @Nullable String attributionTag) { OpEventProxyInfo recycled = acquire(); if (recycled != null) { - recycled.reinit(uid, packageName, featureId); + recycled.reinit(uid, packageName, attributionTag); return recycled; } - return new OpEventProxyInfo(uid, packageName, featureId); + return new OpEventProxyInfo(uid, packageName, attributionTag); } } @@ -666,15 +664,19 @@ public class AppOpsService extends IAppOpsService.Stub { final static class Ops extends SparseArray<Op> { final String packageName; final UidState uidState; - final boolean isPrivileged; - /** Lazily populated cache of featureIds of this package */ - final @NonNull ArraySet<String> knownFeatureIds = new ArraySet<>(); + /** + * The restriction properties of the package. If {@code null} it could not have been read + * yet and has to be refreshed. + */ + @Nullable RestrictionBypass bypass; - Ops(String _packageName, UidState _uidState, boolean _isPrivileged) { + /** Lazily populated cache of attributionTags of this package */ + final @NonNull ArraySet<String> knownAttributionTags = new ArraySet<>(); + + Ops(String _packageName, UidState _uidState) { packageName = _packageName; uidState = _uidState; - isPrivileged = _isPrivileged; } } @@ -774,8 +776,8 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private final class FeatureOp { - public final @Nullable String featureId; + private final class AttributedOp { + public final @Nullable String tag; public final @NonNull Op parent; /** @@ -802,8 +804,8 @@ public class AppOpsService extends IAppOpsService.Stub { @GuardedBy("AppOpsService.this") private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents; - FeatureOp(@Nullable String featureId, @NonNull Op parent) { - this.featureId = featureId; + AttributedOp(@Nullable String tag, @NonNull Op parent) { + this.tag = tag; this.parent = parent; } @@ -812,18 +814,18 @@ public class AppOpsService extends IAppOpsService.Stub { * * @param proxyUid The uid of the proxy * @param proxyPackageName The package name of the proxy - * @param proxyFeatureId the featureId in the proxies package + * @param proxyAttributionTag the attributionTag in the proxies package * @param uidState UID state of the app noteOp/startOp was called for * @param flags OpFlags of the call */ public void accessed(int proxyUid, @Nullable String proxyPackageName, - @Nullable String proxyFeatureId, @AppOpsManager.UidState int uidState, + @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState, @OpFlags int flags) { accessed(System.currentTimeMillis(), -1, proxyUid, proxyPackageName, - proxyFeatureId, uidState, flags); + proxyAttributionTag, uidState, flags); mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName, - featureId, uidState, flags); + tag, uidState, flags); } /** @@ -833,12 +835,12 @@ public class AppOpsService extends IAppOpsService.Stub { * @param duration The duration of the event * @param proxyUid The uid of the proxy * @param proxyPackageName The package name of the proxy - * @param proxyFeatureId the featureId in the proxies package + * @param proxyAttributionTag the attributionTag in the proxies package * @param uidState UID state of the app noteOp/startOp was called for * @param flags OpFlags of the call */ public void accessed(long noteTime, long duration, int proxyUid, - @Nullable String proxyPackageName, @Nullable String proxyFeatureId, + @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState, @OpFlags int flags) { long key = makeKey(uidState, flags); @@ -849,7 +851,7 @@ public class AppOpsService extends IAppOpsService.Stub { OpEventProxyInfo proxyInfo = null; if (proxyUid != Process.INVALID_UID) { proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName, - proxyFeatureId); + proxyAttributionTag); } NoteOpEvent existingEvent = mAccessEvents.get(key); @@ -870,7 +872,7 @@ public class AppOpsService extends IAppOpsService.Stub { rejected(System.currentTimeMillis(), uidState, flags); mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid, parent.packageName, - featureId, uidState, flags); + tag, uidState, flags); } /** @@ -936,7 +938,7 @@ public class AppOpsService extends IAppOpsService.Stub { // startOp events don't support proxy, hence use flags==SELF mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName, - featureId, uidState, OP_FLAG_SELF); + tag, uidState, OP_FLAG_SELF); } /** @@ -976,7 +978,7 @@ public class AppOpsService extends IAppOpsService.Stub { mAccessEvents.put(makeKey(event.getUidState(), OP_FLAG_SELF), finishedEvent); mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid, - parent.packageName, featureId, event.getUidState(), + parent.packageName, tag, event.getUidState(), AppOpsManager.OP_FLAG_SELF, finishedEvent.getDuration()); mInProgressStartOpEventPool.release(event); @@ -984,7 +986,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (mInProgressEvents.isEmpty()) { mInProgressEvents = null; - // TODO moltmann: Also callback for single feature activity changes + // TODO moltmann: Also callback for single attribution tag activity changes if (triggerCallbackIfNeeded && !parent.isRunning()) { scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName, false); @@ -1075,14 +1077,14 @@ public class AppOpsService extends IAppOpsService.Stub { } /** - * Add all data from the {@code featureToAdd} to this op. + * Add all data from the {@code opToAdd} to this op. * * <p>If there is an event for the same key in both the later event is retained. * <p>{@code opToAdd} should not be used after this method is called. * * @param opToAdd The op to add */ - public void add(@NonNull FeatureOp opToAdd) { + public void add(@NonNull AttributedOp opToAdd) { if (opToAdd.mInProgressEvents != null) { Slog.w(TAG, "Ignoring " + opToAdd.mInProgressEvents.size() + " running app-ops"); @@ -1126,7 +1128,7 @@ public class AppOpsService extends IAppOpsService.Stub { return clone; } - @NonNull OpFeatureEntry createFeatureEntryLocked() { + @NonNull AttributedOpEntry createAttributedOpEntryLocked() { LongSparseArray<NoteOpEvent> accessEvents = deepClone(mAccessEvents); // Add in progress events as access events @@ -1150,7 +1152,7 @@ public class AppOpsService extends IAppOpsService.Stub { LongSparseArray<NoteOpEvent> rejectEvents = deepClone(mRejectEvents); - return new OpFeatureEntry(parent.op, isRunning(), accessEvents, rejectEvents); + return new AttributedOpEntry(parent.op, isRunning(), accessEvents, rejectEvents); } } @@ -1162,8 +1164,8 @@ public class AppOpsService extends IAppOpsService.Stub { private @Mode int mode; - /** featureId -> FeatureOp */ - final ArrayMap<String, FeatureOp> mFeatures = new ArrayMap<>(1); + /** attributionTag -> AttributedOp */ + final ArrayMap<String, AttributedOp> mAttributions = new ArrayMap<>(1); Op(UidState uidState, String packageName, int op, int uid) { this.op = op; @@ -1181,58 +1183,59 @@ public class AppOpsService extends IAppOpsService.Stub { return uidState.evalMode(op, mode); } - void removeFeaturesWithNoTime() { - for (int i = mFeatures.size() - 1; i >= 0; i--) { - if (!mFeatures.valueAt(i).hasAnyTime()) { - mFeatures.removeAt(i); + void removeAttributionsWithNoTime() { + for (int i = mAttributions.size() - 1; i >= 0; i--) { + if (!mAttributions.valueAt(i).hasAnyTime()) { + mAttributions.removeAt(i); } } } - private @NonNull FeatureOp getOrCreateFeature(@NonNull Op parent, - @Nullable String featureId) { - FeatureOp featureOp; + private @NonNull AttributedOp getOrCreateAttribution(@NonNull Op parent, + @Nullable String attributionTag) { + AttributedOp attributedOp; - featureOp = mFeatures.get(featureId); - if (featureOp == null) { - featureOp = new FeatureOp(featureId, parent); - mFeatures.put(featureId, featureOp); + attributedOp = mAttributions.get(attributionTag); + if (attributedOp == null) { + attributedOp = new AttributedOp(attributionTag, parent); + mAttributions.put(attributionTag, attributedOp); } - return featureOp; + return attributedOp; } @NonNull OpEntry createEntryLocked() { - final int numFeatures = mFeatures.size(); + final int numAttributions = mAttributions.size(); - final ArrayMap<String, OpFeatureEntry> featureEntries = new ArrayMap<>(numFeatures); - for (int i = 0; i < numFeatures; i++) { - featureEntries.put(mFeatures.keyAt(i), - mFeatures.valueAt(i).createFeatureEntryLocked()); + final ArrayMap<String, AppOpsManager.AttributedOpEntry> attributionEntries = + new ArrayMap<>(numAttributions); + for (int i = 0; i < numAttributions; i++) { + attributionEntries.put(mAttributions.keyAt(i), + mAttributions.valueAt(i).createAttributedOpEntryLocked()); } - return new OpEntry(op, mode, featureEntries); + return new OpEntry(op, mode, attributionEntries); } - @NonNull OpEntry createSingleFeatureEntryLocked(@Nullable String featureId) { - final int numFeatures = mFeatures.size(); + @NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) { + final int numAttributions = mAttributions.size(); - final ArrayMap<String, OpFeatureEntry> featureEntries = new ArrayMap<>(1); - for (int i = 0; i < numFeatures; i++) { - if (Objects.equals(mFeatures.keyAt(i), featureId)) { - featureEntries.put(mFeatures.keyAt(i), - mFeatures.valueAt(i).createFeatureEntryLocked()); + final ArrayMap<String, AttributedOpEntry> attributionEntries = new ArrayMap<>(1); + for (int i = 0; i < numAttributions; i++) { + if (Objects.equals(mAttributions.keyAt(i), attributionTag)) { + attributionEntries.put(mAttributions.keyAt(i), + mAttributions.valueAt(i).createAttributedOpEntryLocked()); break; } } - return new OpEntry(op, mode, featureEntries); + return new OpEntry(op, mode, attributionEntries); } boolean isRunning() { - final int numFeatures = mFeatures.size(); - for (int i = 0; i < numFeatures; i++) { - if (mFeatures.valueAt(i).isRunning()) { + final int numAttributions = mAttributions.size(); + for (int i = 0; i < numAttributions; i++) { + if (mAttributions.valueAt(i).isRunning()) { return true; } } @@ -1405,10 +1408,11 @@ public class AppOpsService extends IAppOpsService.Stub { } /** - * Call {@link FeatureOp#onClientDeath featureOp.onClientDeath(clientId)}. + * Call {@link AttributedOp#onClientDeath attributedOp.onClientDeath(clientId)}. */ - private static void onClientDeath(@NonNull FeatureOp featureOp, @NonNull IBinder clientId) { - featureOp.onClientDeath(clientId); + private static void onClientDeath(@NonNull AttributedOp attributedOp, + @NonNull IBinder clientId) { + attributedOp.onClientDeath(clientId); } @@ -1490,20 +1494,21 @@ public class AppOpsService extends IAppOpsService.Stub { return; } - ArrayMap<String, String> dstFeatureIds = new ArrayMap<>(); - ArraySet<String> featureIds = new ArraySet<>(); - featureIds.add(null); - if (pkg.getFeatures() != null) { - int numFeatures = pkg.getFeatures().size(); - for (int featureNum = 0; featureNum < numFeatures; featureNum++) { - ParsedFeature feature = pkg.getFeatures().get(featureNum); - featureIds.add(feature.id); + ArrayMap<String, String> dstAttributionTags = new ArrayMap<>(); + ArraySet<String> attributionTags = new ArraySet<>(); + attributionTags.add(null); + if (pkg.getAttributions() != null) { + int numAttributions = pkg.getAttributions().size(); + for (int attributionNum = 0; attributionNum < numAttributions; + attributionNum++) { + ParsedAttribution attribution = pkg.getAttributions().get(attributionNum); + attributionTags.add(attribution.tag); - int numInheritFrom = feature.inheritFrom.size(); + int numInheritFrom = attribution.inheritFrom.size(); for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) { - dstFeatureIds.put(feature.inheritFrom.get(inheritFromNum), - feature.id); + dstAttributionTags.put(attribution.inheritFrom.get(inheritFromNum), + attribution.tag); } } } @@ -1519,25 +1524,32 @@ public class AppOpsService extends IAppOpsService.Stub { return; } - ops.knownFeatureIds.clear(); + // Reset cached package properties to re-initialize when needed + ops.bypass = null; + ops.knownAttributionTags.clear(); + + // Merge data collected for removed attributions into their successor + // attributions int numOps = ops.size(); for (int opNum = 0; opNum < numOps; opNum++) { Op op = ops.valueAt(opNum); - int numFeatures = op.mFeatures.size(); - for (int featureNum = numFeatures - 1; featureNum >= 0; featureNum--) { - String featureId = op.mFeatures.keyAt(featureNum); + int numAttributions = op.mAttributions.size(); + for (int attributionNum = numAttributions - 1; attributionNum >= 0; + attributionNum--) { + String attributionTag = op.mAttributions.keyAt(attributionNum); - if (featureIds.contains(featureId)) { - // feature still exist after upgrade + if (attributionTags.contains(attributionTag)) { + // attribution still exist after upgrade continue; } - String newFeatureId = dstFeatureIds.get(featureId); + String newAttributionTag = dstAttributionTags.get(attributionTag); - FeatureOp newFeatureOp = op.getOrCreateFeature(op, newFeatureId); - newFeatureOp.add(op.mFeatures.valueAt(featureNum)); - op.mFeatures.removeAt(featureNum); + AttributedOp newAttributedOp = op.getOrCreateAttribution(op, + newAttributionTag); + newAttributedOp.add(op.mAttributions.valueAt(attributionNum)); + op.mAttributions.removeAt(attributionNum); scheduleFastWriteLocked(); } @@ -1737,12 +1749,13 @@ public class AppOpsService extends IAppOpsService.Stub { for (int opNum = 0; opNum < numOps; opNum++) { final Op op = ops.valueAt(opNum); - final int numFeatures = op.mFeatures.size(); - for (int featureNum = 0; featureNum < numFeatures; featureNum++) { - FeatureOp featureOp = op.mFeatures.valueAt(featureNum); + final int numAttributions = op.mAttributions.size(); + for (int attributionNum = 0; attributionNum < numAttributions; + attributionNum++) { + AttributedOp attributedOp = op.mAttributions.valueAt(attributionNum); - while (featureOp.mInProgressEvents != null) { - featureOp.finished(featureOp.mInProgressEvents.keyAt(0)); + while (attributedOp.mInProgressEvents != null) { + attributedOp.finished(attributedOp.mInProgressEvents.keyAt(0)); } } } @@ -1822,11 +1835,13 @@ public class AppOpsService extends IAppOpsService.Stub { for (int opNum = 0; opNum < numOps; opNum++) { Op op = ops.valueAt(opNum); - int numFeatures = op.mFeatures.size(); - for (int featureNum = 0; featureNum < numFeatures; featureNum++) { - FeatureOp featureOp = op.mFeatures.valueAt(featureNum); + int numAttributions = op.mAttributions.size(); + for (int attributionNum = 0; attributionNum < numAttributions; + attributionNum++) { + AttributedOp attributedOp = op.mAttributions.valueAt( + attributionNum); - featureOp.onUidStateChanged(newState); + attributedOp.onUidStateChanged(newState); } } } @@ -1953,8 +1968,7 @@ public class AppOpsService extends IAppOpsService.Stub { return Collections.emptyList(); } synchronized (this) { - Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false /* isPrivileged */, - false /* edit */); + Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false /* edit */); if (pkgOps == null) { return null; } @@ -1973,9 +1987,9 @@ public class AppOpsService extends IAppOpsService.Stub { /** * Verify that historical appop request arguments are valid. */ - private void ensureHistoricalOpRequestIsValid(int uid, String packageName, String featureId, - List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis, - int flags) { + private void ensureHistoricalOpRequestIsValid(int uid, String packageName, + String attributionTag, List<String> opNames, int filter, long beginTimeMillis, + long endTimeMillis, int flags) { if ((filter & FILTER_BY_UID) != 0) { Preconditions.checkArgument(uid != Process.INVALID_UID); } else { @@ -1988,8 +2002,8 @@ public class AppOpsService extends IAppOpsService.Stub { Preconditions.checkArgument(packageName == null); } - if ((filter & FILTER_BY_FEATURE_ID) == 0) { - Preconditions.checkArgument(featureId == null); + if ((filter & FILTER_BY_ATTRIBUTION_TAG) == 0) { + Preconditions.checkArgument(attributionTag == null); } if ((filter & FILTER_BY_OP_NAMES) != 0) { @@ -1999,17 +2013,18 @@ public class AppOpsService extends IAppOpsService.Stub { } Preconditions.checkFlagsArgument(filter, - FILTER_BY_UID | FILTER_BY_PACKAGE_NAME | FILTER_BY_FEATURE_ID | FILTER_BY_OP_NAMES); + FILTER_BY_UID | FILTER_BY_PACKAGE_NAME | FILTER_BY_ATTRIBUTION_TAG + | FILTER_BY_OP_NAMES); Preconditions.checkArgumentNonnegative(beginTimeMillis); Preconditions.checkArgument(endTimeMillis > beginTimeMillis); Preconditions.checkFlagsArgument(flags, OP_FLAGS_ALL); } @Override - public void getHistoricalOps(int uid, String packageName, String featureId, + public void getHistoricalOps(int uid, String packageName, String attributionTag, List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis, int flags, RemoteCallback callback) { - ensureHistoricalOpRequestIsValid(uid, packageName, featureId, opNames, filter, + ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter, beginTimeMillis, endTimeMillis, flags); Objects.requireNonNull(callback, "callback cannot be null"); @@ -2030,15 +2045,15 @@ public class AppOpsService extends IAppOpsService.Stub { // Must not hold the appops lock mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps, - mHistoricalRegistry, uid, packageName, featureId, opNamesArray, filter, + mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse()); } @Override - public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String featureId, + public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag, List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis, int flags, RemoteCallback callback) { - ensureHistoricalOpRequestIsValid(uid, packageName, featureId, opNames, filter, + ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter, beginTimeMillis, endTimeMillis, flags); Objects.requireNonNull(callback, "callback cannot be null"); @@ -2050,7 +2065,7 @@ public class AppOpsService extends IAppOpsService.Stub { // Must not hold the appops lock mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw, - mHistoricalRegistry, uid, packageName, featureId, opNamesArray, + mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse()); } @@ -2084,11 +2099,10 @@ public class AppOpsService extends IAppOpsService.Stub { } private void pruneOpLocked(Op op, int uid, String packageName) { - op.removeFeaturesWithNoTime(); + op.removeAttributionsWithNoTime(); - if (op.mFeatures.size() == 0) { - Ops ops = getOpsRawLocked(uid, packageName, null, false /* isPrivileged */, - false /* edit */); + if (op.mAttributions.isEmpty()) { + Ops ops = getOpsLocked(uid, packageName, null, null, false /* edit */); if (ops != null) { ops.remove(op.op); if (ops.size() <= 0) { @@ -2390,9 +2404,9 @@ public class AppOpsService extends IAppOpsService.Stub { ArraySet<ModeCallback> repCbs = null; code = AppOpsManager.opToSwitch(code); - boolean isPrivileged; + RestrictionBypass bypass; try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null); + bypass = verifyAndGetBypass(uid, packageName, null); } catch (SecurityException e) { Slog.e(TAG, "Cannot setMode", e); return; @@ -2400,7 +2414,7 @@ public class AppOpsService extends IAppOpsService.Stub { synchronized (this) { UidState uidState = getUidStateLocked(uid, false); - Op op = getOpLocked(code, uid, packageName, null, isPrivileged, true); + Op op = getOpLocked(code, uid, packageName, null, bypass, true); if (op != null) { if (op.mode != mode) { op.mode = mode; @@ -2606,8 +2620,8 @@ public class AppOpsService extends IAppOpsService.Stub { callbacks = addCallbacks(callbacks, curOp.op, uid, packageName, mPackageModeWatchers.get(packageName)); - curOp.removeFeaturesWithNoTime(); - if (curOp.mFeatures.size() == 0) { + curOp.removeAttributionsWithNoTime(); + if (curOp.mAttributions.isEmpty()) { pkgOps.removeAt(j); } } @@ -2674,10 +2688,8 @@ public class AppOpsService extends IAppOpsService.Stub { synchronized (this) { int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op; - // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE int notifiedOps; - if (Compatibility.isChangeEnabled( - CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE)) { + if ((flags & CALL_BACK_ON_SWITCHED_OP) == 0) { if (op == OP_NONE) { notifiedOps = ALL_OPS; } else { @@ -2798,10 +2810,9 @@ public class AppOpsService extends IAppOpsService.Stub { */ private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName, boolean raw) { - boolean isPrivileged; - + RestrictionBypass bypass; try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null); + bypass = verifyAndGetBypass(uid, packageName, null); } catch (SecurityException e) { Slog.e(TAG, "checkOperation", e); return AppOpsManager.opToDefaultMode(code); @@ -2811,7 +2822,7 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } synchronized (this) { - if (isOpRestrictedLocked(uid, code, packageName, null, isPrivileged)) { + if (isOpRestrictedLocked(uid, code, packageName, bypass)) { return AppOpsManager.MODE_IGNORED; } code = AppOpsManager.opToSwitch(code); @@ -2821,7 +2832,7 @@ public class AppOpsService extends IAppOpsService.Stub { final int rawMode = uidState.opModes.get(code); return raw ? rawMode : uidState.evalMode(code, rawMode); } - Op op = getOpLocked(code, uid, packageName, null, false, false); + Op op = getOpLocked(code, uid, packageName, null, bypass, false); if (op == null) { return AppOpsManager.opToDefaultMode(code); } @@ -2884,7 +2895,7 @@ public class AppOpsService extends IAppOpsService.Stub { public int checkPackage(int uid, String packageName) { Objects.requireNonNull(packageName); try { - verifyAndGetIsPrivileged(uid, packageName, null); + verifyAndGetBypass(uid, packageName, null); return AppOpsManager.MODE_ALLOWED; } catch (SecurityException ignored) { @@ -2894,8 +2905,8 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName, - String proxiedFeatureId, int proxyUid, String proxyPackageName, - String proxyFeatureId, boolean shouldCollectAsyncNotedOp, String message) { + String proxiedAttributionTag, int proxyUid, String proxyPackageName, + String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message) { verifyIncomingUid(proxyUid); verifyIncomingOp(code); @@ -2911,7 +2922,7 @@ public class AppOpsService extends IAppOpsService.Stub { final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY; final int proxyMode = noteOperationUnchecked(code, proxyUid, resolveProxyPackageName, - proxyFeatureId, Process.INVALID_UID, null, null, proxyFlags, + proxyAttributionTag, Process.INVALID_UID, null, null, proxyFlags, !isProxyTrusted, "proxy " + message); if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) { return proxyMode; @@ -2924,27 +2935,27 @@ public class AppOpsService extends IAppOpsService.Stub { final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED : AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED; return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName, - proxiedFeatureId, proxyUid, resolveProxyPackageName, proxyFeatureId, + proxiedAttributionTag, proxyUid, resolveProxyPackageName, proxyAttributionTag, proxiedFlags, shouldCollectAsyncNotedOp, message); } @Override - public int noteOperation(int code, int uid, String packageName, String featureId, + public int noteOperation(int code, int uid, String packageName, String attributionTag, boolean shouldCollectAsyncNotedOp, String message) { final CheckOpsDelegate delegate; synchronized (this) { delegate = mCheckOpsDelegate; } if (delegate == null) { - return noteOperationImpl(code, uid, packageName, featureId, shouldCollectAsyncNotedOp, - message); + return noteOperationImpl(code, uid, packageName, attributionTag, + shouldCollectAsyncNotedOp, message); } - return delegate.noteOperation(code, uid, packageName, featureId, shouldCollectAsyncNotedOp, - message, AppOpsService.this::noteOperationImpl); + return delegate.noteOperation(code, uid, packageName, attributionTag, + shouldCollectAsyncNotedOp, message, AppOpsService.this::noteOperationImpl); } private int noteOperationImpl(int code, int uid, @Nullable String packageName, - @Nullable String featureId, boolean shouldCollectAsyncNotedOp, + @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable String message) { verifyIncomingUid(uid); verifyIncomingOp(code); @@ -2952,25 +2963,25 @@ public class AppOpsService extends IAppOpsService.Stub { if (resolvedPackageName == null) { return AppOpsManager.MODE_IGNORED; } - return noteOperationUnchecked(code, uid, resolvedPackageName, featureId, + return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag, Process.INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF, shouldCollectAsyncNotedOp, message); } private int noteOperationUnchecked(int code, int uid, @NonNull String packageName, - @Nullable String featureId, int proxyUid, String proxyPackageName, - @Nullable String proxyFeatureId, @OpFlags int flags, boolean shouldCollectAsyncNotedOp, - @Nullable String message) { - boolean isPrivileged; + @Nullable String attributionTag, int proxyUid, String proxyPackageName, + @Nullable String proxyAttributionTag, @OpFlags int flags, + boolean shouldCollectAsyncNotedOp, @Nullable String message) { + RestrictionBypass bypass; try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId); + bypass = verifyAndGetBypass(uid, packageName, attributionTag); } catch (SecurityException e) { Slog.e(TAG, "noteOperation", e); return AppOpsManager.MODE_ERRORED; } synchronized (this) { - final Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged, + final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */); if (ops == null) { scheduleOpNotedIfNeededLocked(code, uid, packageName, @@ -2980,17 +2991,17 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, uid, true); - final FeatureOp featureOp = op.getOrCreateFeature(op, featureId); - if (isOpRestrictedLocked(uid, code, packageName, featureId, isPrivileged)) { + final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag); + if (isOpRestrictedLocked(uid, code, packageName, bypass)) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); return AppOpsManager.MODE_IGNORED; } final UidState uidState = ops.uidState; - if (featureOp.isRunning()) { + if (attributedOp.isRunning()) { Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code " + code + " startTime of in progress event=" - + featureOp.mInProgressEvents.valueAt(0).getStartTime()); + + attributedOp.mInProgressEvents.valueAt(0).getStartTime()); } final int switchCode = AppOpsManager.opToSwitch(code); @@ -3002,7 +3013,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName); - featureOp.rejected(uidState.state, flags); + attributedOp.rejected(uidState.state, flags); scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode); return uidMode; } @@ -3014,26 +3025,31 @@ public class AppOpsService extends IAppOpsService.Stub { if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName); - featureOp.rejected(uidState.state, flags); + attributedOp.rejected(uidState.state, flags); scheduleOpNotedIfNeededLocked(code, uid, packageName, mode); return mode; } } - if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid - + " package " + packageName + (featureId == null ? "" : "." + featureId)); - featureOp.accessed(proxyUid, proxyPackageName, proxyFeatureId, uidState.state, flags); + if (DEBUG) { + Slog.d(TAG, + "noteOperation: allowing code " + code + " uid " + uid + " package " + + packageName + (attributionTag == null ? "" + : "." + attributionTag)); + } + attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag, uidState.state, + flags); scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED); if (shouldCollectAsyncNotedOp) { - collectAsyncNotedOp(uid, packageName, code, featureId, message); + collectAsyncNotedOp(uid, packageName, code, attributionTag, message); } return AppOpsManager.MODE_ALLOWED; } } - // TODO moltmann: Allow watching for feature ops + // TODO moltmann: Allow watching for attribution ops @Override public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) { int watchedUid = -1; @@ -3131,11 +3147,11 @@ public class AppOpsService extends IAppOpsService.Stub { * @param uid The uid the op was noted for * @param packageName The package the op was noted for * @param opCode The code of the op noted - * @param featureId The id of the feature to op was noted for + * @param attributionTag attribution tag the op was noted for * @param message The message for the op noting */ private void collectAsyncNotedOp(int uid, @NonNull String packageName, int opCode, - @Nullable String featureId, @NonNull String message) { + @Nullable String attributionTag, @NonNull String message) { Objects.requireNonNull(message); int callingUid = Binder.getCallingUid(); @@ -3147,10 +3163,10 @@ public class AppOpsService extends IAppOpsService.Stub { RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key); AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid, - featureId, message, System.currentTimeMillis()); + attributionTag, message, System.currentTimeMillis()); final boolean[] wasNoteForwarded = {false}; - reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode, featureId, + reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode, attributionTag, message); if (callbacks != null) { @@ -3161,7 +3177,7 @@ public class AppOpsService extends IAppOpsService.Stub { } catch (RemoteException e) { Slog.e(TAG, "Could not forward noteOp of " + opCode + " to " + packageName - + "/" + uid + "(" + featureId + ")", e); + + "/" + uid + "(" + attributionTag + ")", e); } }); } @@ -3205,7 +3221,7 @@ public class AppOpsService extends IAppOpsService.Stub { int uid = Binder.getCallingUid(); Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid); - verifyAndGetIsPrivileged(uid, packageName, null); + verifyAndGetBypass(uid, packageName, null); synchronized (this) { RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key); @@ -3235,7 +3251,7 @@ public class AppOpsService extends IAppOpsService.Stub { int uid = Binder.getCallingUid(); Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid); - verifyAndGetIsPrivileged(uid, packageName, null); + verifyAndGetBypass(uid, packageName, null); synchronized (this) { RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key); @@ -3254,7 +3270,7 @@ public class AppOpsService extends IAppOpsService.Stub { int uid = Binder.getCallingUid(); - verifyAndGetIsPrivileged(uid, packageName, null); + verifyAndGetBypass(uid, packageName, null); synchronized (this) { return mUnforwardedAsyncNotedOps.remove(getAsyncNotedOpsKey(packageName, uid)); @@ -3263,7 +3279,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int startOperation(IBinder clientId, int code, int uid, String packageName, - String featureId, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, + String attributionTag, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message) { verifyIncomingUid(uid); verifyIncomingOp(code); @@ -3272,16 +3288,16 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } - boolean isPrivileged; + RestrictionBypass bypass; try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId); + bypass = verifyAndGetBypass(uid, packageName, attributionTag); } catch (SecurityException e) { Slog.e(TAG, "startOperation", e); return AppOpsManager.MODE_ERRORED; } synchronized (this) { - final Ops ops = getOpsRawLocked(uid, resolvedPackageName, featureId, isPrivileged, + final Ops ops = getOpsLocked(uid, resolvedPackageName, attributionTag, bypass, true /* edit */); if (ops == null) { if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid @@ -3289,10 +3305,10 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, uid, true); - if (isOpRestrictedLocked(uid, code, resolvedPackageName, featureId, isPrivileged)) { + if (isOpRestrictedLocked(uid, code, resolvedPackageName, bypass)) { return AppOpsManager.MODE_IGNORED; } - final FeatureOp featureOp = op.getOrCreateFeature(op, featureId); + final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag); final int switchCode = AppOpsManager.opToSwitch(code); final UidState uidState = ops.uidState; // If there is a non-default per UID policy (we set UID op mode only if @@ -3305,7 +3321,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + resolvedPackageName); - featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF); + attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF); return uidMode; } } else { @@ -3317,21 +3333,21 @@ public class AppOpsService extends IAppOpsService.Stub { if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + resolvedPackageName); - featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF); + attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF); return mode; } } if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid + " package " + resolvedPackageName); try { - featureOp.started(clientId, uidState.state); + attributedOp.started(clientId, uidState.state); } catch (RemoteException e) { throw new RuntimeException(e); } } if (shouldCollectAsyncNotedOp) { - collectAsyncNotedOp(uid, packageName, code, featureId, message); + collectAsyncNotedOp(uid, packageName, code, attributionTag, message); } return AppOpsManager.MODE_ALLOWED; @@ -3339,7 +3355,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void finishOperation(IBinder clientId, int code, int uid, String packageName, - String featureId) { + String attributionTag) { verifyIncomingUid(uid); verifyIncomingOp(code); String resolvedPackageName = resolvePackageName(uid, packageName); @@ -3347,26 +3363,26 @@ public class AppOpsService extends IAppOpsService.Stub { return; } - boolean isPrivileged; + RestrictionBypass bypass; try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId); + bypass = verifyAndGetBypass(uid, packageName, attributionTag); } catch (SecurityException e) { Slog.e(TAG, "Cannot finishOperation", e); return; } synchronized (this) { - Op op = getOpLocked(code, uid, resolvedPackageName, featureId, isPrivileged, true); + Op op = getOpLocked(code, uid, resolvedPackageName, attributionTag, bypass, true); if (op == null) { return; } - final FeatureOp featureOp = op.mFeatures.get(featureId); - if (featureOp == null) { + final AttributedOp attributedOp = op.mAttributions.get(attributionTag); + if (attributedOp == null) { return; } try { - featureOp.finished(clientId); + attributedOp.finished(clientId); } catch (IllegalStateException e) { Slog.e(TAG, "Operation not started: uid=" + uid + " pkg=" + packageName + " op=" + AppOpsManager.opToName(code), e); @@ -3617,50 +3633,66 @@ public class AppOpsService extends IAppOpsService.Stub { } /** - * Verify that package belongs to uid and return whether the package is privileged. + * Create a restriction description matching the properties of the package. + * + * @param context A context to use + * @param pkg The package to create the restriction description for + * + * @return The restriction matching the package + */ + private RestrictionBypass getBypassforPackage(@NonNull AndroidPackage pkg) { + return new RestrictionBypass(pkg.isPrivileged(), mContext.checkPermission( + android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid()) + == PackageManager.PERMISSION_GRANTED); + } + + /** + * Verify that package belongs to uid and return the {@link RestrictionBypass bypass + * description} for the package. * * @param uid The uid the package belongs to * @param packageName The package the might belong to the uid - * @param featureId The feature in the package or {@code null} if no need to verify + * @param attributionTag attribution tag or {@code null} if no need to verify * * @return {@code true} iff the package is privileged */ - private boolean verifyAndGetIsPrivileged(int uid, String packageName, - @Nullable String featureId) { + private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName, + @Nullable String attributionTag) { if (uid == Process.ROOT_UID) { // For backwards compatibility, don't check package name for root UID. - return false; + return null; } - // Do not check if uid/packageName/featureId is already known + // Do not check if uid/packageName/attributionTag is already known synchronized (this) { UidState uidState = mUidStates.get(uid); if (uidState != null && uidState.pkgOps != null) { Ops ops = uidState.pkgOps.get(packageName); - if (ops != null && (featureId == null || ops.knownFeatureIds.contains(featureId))) { - return ops.isPrivileged; + if (ops != null && (attributionTag == null || ops.knownAttributionTags.contains( + attributionTag)) && ops.bypass != null) { + return ops.bypass; } } } - boolean isPrivileged = false; + RestrictionBypass bypass = null; final long ident = Binder.clearCallingIdentity(); try { int pkgUid; AndroidPackage pkg = LocalServices.getService(PackageManagerInternal.class).getPackage( packageName); - boolean isFeatureIdValid = false; + boolean isAttributionTagValid = false; if (pkg != null) { - if (featureId == null) { - isFeatureIdValid = true; + if (attributionTag == null) { + isAttributionTagValid = true; } else { - if (pkg.getFeatures() != null) { - int numFeatures = pkg.getFeatures().size(); - for (int i = 0; i < numFeatures; i++) { - if (pkg.getFeatures().get(i).id.equals(featureId)) { - isFeatureIdValid = true; + if (pkg.getAttributions() != null) { + int numAttributions = pkg.getAttributions().size(); + for (int i = 0; i < numAttributions; i++) { + if (pkg.getAttributions().get(i).tag.equals(attributionTag)) { + isAttributionTagValid = true; } } } @@ -3668,14 +3700,14 @@ public class AppOpsService extends IAppOpsService.Stub { pkgUid = UserHandle.getUid( UserHandle.getUserId(uid), UserHandle.getAppId(pkg.getUid())); - isPrivileged = pkg.isPrivileged(); + bypass = getBypassforPackage(pkg); } else { - // Allow any feature id for resolvable uids - isFeatureIdValid = true; + // Allow any attribution tag for resolvable uids + isAttributionTagValid = true; pkgUid = resolveUid(packageName); if (pkgUid >= 0) { - isPrivileged = false; + bypass = RestrictionBypass.UNRESTRICTED; } } if (pkgUid != uid) { @@ -3683,16 +3715,16 @@ public class AppOpsService extends IAppOpsService.Stub { + " but it is really " + pkgUid); } - if (!isFeatureIdValid) { + if (!isAttributionTagValid) { // TODO moltmann: Switch from logging to enforcement - Slog.e(TAG, "featureId " + featureId + " not declared in manifest of " + Slog.e(TAG, "attributionTag " + attributionTag + " not declared in manifest of " + packageName); } } finally { Binder.restoreCallingIdentity(ident); } - return isPrivileged; + return bypass; } /** @@ -3700,14 +3732,14 @@ public class AppOpsService extends IAppOpsService.Stub { * * @param uid The uid the package belongs to * @param packageName The name of the package - * @param featureId The feature in the package - * @param isPrivileged If the package is privilidged (ignored if {@code edit} is false) + * @param attributionTag attribution tag + * @param bypass When to bypass certain op restrictions (can be null if edit == false) * @param edit If an ops does not exist, create the ops? - * @return + * @return The ops */ - private Ops getOpsRawLocked(int uid, String packageName, @Nullable String featureId, - boolean isPrivileged, boolean edit) { + private Ops getOpsLocked(int uid, String packageName, @Nullable String attributionTag, + @Nullable RestrictionBypass bypass, boolean edit) { UidState uidState = getUidStateLocked(uid, edit); if (uidState == null) { return null; @@ -3725,53 +3757,18 @@ public class AppOpsService extends IAppOpsService.Stub { if (!edit) { return null; } - ops = new Ops(packageName, uidState, isPrivileged); + ops = new Ops(packageName, uidState); uidState.pkgOps.put(packageName, ops); } - if (edit && featureId != null) { - ops.knownFeatureIds.add(featureId); - } - return ops; - } - - /** - * Get the state of all ops for a package. - * - * <p>Usually callers should use {@link #getOpLocked} and not call this directly. - * - * @param uid The uid the of the package - * @param packageName The package name for which to get the state for - * @param featureId The feature in the package - * @param edit Iff {@code true} create the {@link Ops} object if not yet created - * @param isPrivileged Whether the package is privileged or not - * - * @return The {@link Ops state} of all ops for the package - */ - private @Nullable Ops getOpsRawNoVerifyLocked(int uid, @NonNull String packageName, - @Nullable String featureId, boolean edit, boolean isPrivileged) { - UidState uidState = getUidStateLocked(uid, edit); - if (uidState == null) { - return null; - } - if (uidState.pkgOps == null) { - if (!edit) { - return null; + if (edit) { + if (bypass != null) { + ops.bypass = bypass; } - uidState.pkgOps = new ArrayMap<>(); - } - Ops ops = uidState.pkgOps.get(packageName); - if (ops == null) { - if (!edit) { - return null; + if (attributionTag != null) { + ops.knownAttributionTags.add(attributionTag); } - ops = new Ops(packageName, uidState, isPrivileged); - uidState.pkgOps.put(packageName, ops); - } - - if (edit && featureId != null) { - ops.knownFeatureIds.add(featureId); } return ops; @@ -3799,16 +3796,15 @@ public class AppOpsService extends IAppOpsService.Stub { * @param code The code of the op * @param uid The uid the of the package * @param packageName The package name for which to get the state for - * @param featureId The feature in the package - * @param isPrivileged Whether the package is privileged or not (only used if {@code edit - * == true}) + * @param attributionTag The attribution tag + * @param bypass When to bypass certain op restrictions (can be null if edit == false) * @param edit Iff {@code true} create the {@link Op} object if not yet created * * @return The {@link Op state} of the op */ private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, - @Nullable String featureId, boolean isPrivileged, boolean edit) { - Ops ops = getOpsRawNoVerifyLocked(uid, packageName, featureId, edit, isPrivileged); + @Nullable String attributionTag, @Nullable RestrictionBypass bypass, boolean edit) { + Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, edit); if (ops == null) { return null; } @@ -3839,7 +3835,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private boolean isOpRestrictedLocked(int uid, int code, String packageName, - @Nullable String featureId, boolean isPrivileged) { + @Nullable RestrictionBypass appBypass) { int userHandle = UserHandle.getUserId(uid); final int restrictionSetCount = mOpUserRestrictions.size(); @@ -3848,12 +3844,15 @@ public class AppOpsService extends IAppOpsService.Stub { // package is exempt from the restriction. ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); if (restrictionState.hasRestriction(code, packageName, userHandle)) { - if (AppOpsManager.opAllowSystemBypassRestriction(code)) { + RestrictionBypass opBypass = opAllowSystemBypassRestriction(code); + if (opBypass != null) { // If we are the system, bypass user restrictions for certain codes synchronized (this) { - Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged, - true /* edit */); - if ((ops != null) && ops.isPrivileged) { + if (opBypass.isPrivileged && appBypass != null && appBypass.isPrivileged) { + return false; + } + if (opBypass.isRecordAudioRestrictionExcept && appBypass != null + && appBypass.isRecordAudioRestrictionExcept) { return false; } } @@ -4043,28 +4042,6 @@ public class AppOpsService extends IAppOpsService.Stub { throws NumberFormatException, XmlPullParserException, IOException { int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); final UidState uidState = getUidStateLocked(uid, true); - String isPrivilegedString = parser.getAttributeValue(null, "p"); - boolean isPrivileged = false; - if (isPrivilegedString == null) { - try { - IPackageManager packageManager = ActivityThread.getPackageManager(); - if (packageManager != null) { - ApplicationInfo appInfo = ActivityThread.getPackageManager() - .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid)); - if (appInfo != null) { - isPrivileged = (appInfo.privateFlags - & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; - } - } else { - // Could not load data, don't add to cache so it will be loaded later. - return; - } - } catch (RemoteException e) { - Slog.w(TAG, "Could not contact PackageManager", e); - } - } else { - isPrivileged = Boolean.parseBoolean(isPrivilegedString); - } int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -4074,7 +4051,7 @@ public class AppOpsService extends IAppOpsService.Stub { } String tagName = parser.getName(); if (tagName.equals("op")) { - readOp(parser, uidState, pkgName, isPrivileged); + readOp(parser, uidState, pkgName); } else { Slog.w(TAG, "Unknown element under <pkg>: " + parser.getName()); @@ -4084,9 +4061,9 @@ public class AppOpsService extends IAppOpsService.Stub { uidState.evalForegroundOps(mOpModeWatchers); } - private void readFeatureOp(XmlPullParser parser, @NonNull Op parent, - @Nullable String feature) throws NumberFormatException, IOException { - final FeatureOp featureOp = parent.getOrCreateFeature(parent, feature); + private void readAttributionOp(XmlPullParser parser, @NonNull Op parent, + @Nullable String attribution) throws NumberFormatException, IOException { + final AttributedOp attributedOp = parent.getOrCreateAttribution(parent, attribution); final long key = XmlUtils.readLongAttribute(parser, "n"); final int uidState = extractUidStateFromKey(key); @@ -4097,19 +4074,19 @@ public class AppOpsService extends IAppOpsService.Stub { final long accessDuration = XmlUtils.readLongAttribute(parser, "d", -1); final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp"); final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", Process.INVALID_UID); - final String proxyFeatureId = XmlUtils.readStringAttribute(parser, "pc"); + final String proxyAttributionTag = XmlUtils.readStringAttribute(parser, "pc"); if (accessTime > 0) { - featureOp.accessed(accessTime, accessDuration, proxyUid, proxyPkg, proxyFeatureId, - uidState, opFlags); + attributedOp.accessed(accessTime, accessDuration, proxyUid, proxyPkg, + proxyAttributionTag, uidState, opFlags); } if (rejectTime > 0) { - featureOp.rejected(rejectTime, uidState, opFlags); + attributedOp.rejected(rejectTime, uidState, opFlags); } } - private void readOp(XmlPullParser parser, @NonNull UidState uidState, - @NonNull String pkgName, boolean isPrivileged) throws NumberFormatException, + private void readOp(XmlPullParser parser, @NonNull UidState uidState, @NonNull String pkgName) + throws NumberFormatException, XmlPullParserException, IOException { int opCode = Integer.parseInt(parser.getAttributeValue(null, "n")); if (isIgnoredAppOp(opCode)) { @@ -4130,7 +4107,7 @@ public class AppOpsService extends IAppOpsService.Stub { } String tagName = parser.getName(); if (tagName.equals("st")) { - readFeatureOp(parser, op, XmlUtils.readStringAttribute(parser, "id")); + readAttributionOp(parser, op, XmlUtils.readStringAttribute(parser, "id")); } else { Slog.w(TAG, "Unknown element under <op>: " + parser.getName()); @@ -4143,7 +4120,7 @@ public class AppOpsService extends IAppOpsService.Stub { } Ops ops = uidState.pkgOps.get(pkgName); if (ops == null) { - ops = new Ops(pkgName, uidState, isPrivileged); + ops = new Ops(pkgName, uidState); uidState.pkgOps.put(pkgName, ops); } ops.put(op.op, op); @@ -4235,17 +4212,6 @@ public class AppOpsService extends IAppOpsService.Stub { } out.startTag(null, "uid"); out.attribute(null, "n", Integer.toString(pkg.getUid())); - synchronized (this) { - Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), null, - false /* isPrivileged */, false /* edit */); - // Should always be present as the list of PackageOps is generated - // from Ops. - if (ops != null) { - out.attribute(null, "p", Boolean.toString(ops.isPrivileged)); - } else { - out.attribute(null, "p", Boolean.toString(false)); - } - } List<AppOpsManager.OpEntry> ops = pkg.getOps(); for (int j=0; j<ops.size(); j++) { AppOpsManager.OpEntry op = ops.get(j); @@ -4255,11 +4221,11 @@ public class AppOpsService extends IAppOpsService.Stub { out.attribute(null, "m", Integer.toString(op.getMode())); } - for (String featureId : op.getFeatures().keySet()) { - final OpFeatureEntry feature = op.getFeatures().get( - featureId); + for (String attributionTag : op.getAttributedOpEntries().keySet()) { + final AttributedOpEntry attribution = + op.getAttributedOpEntries().get(attributionTag); - final ArraySet<Long> keys = feature.collectKeys(); + final ArraySet<Long> keys = attribution.collectKeys(); final int keyCount = keys.size(); for (int k = 0; k < keyCount; k++) { @@ -4268,14 +4234,14 @@ public class AppOpsService extends IAppOpsService.Stub { final int uidState = AppOpsManager.extractUidStateFromKey(key); final int flags = AppOpsManager.extractFlagsFromKey(key); - final long accessTime = feature.getLastAccessTime(uidState, + final long accessTime = attribution.getLastAccessTime(uidState, uidState, flags); - final long rejectTime = feature.getLastRejectTime(uidState, - uidState, flags); - final long accessDuration = feature.getLastDuration(uidState, + final long rejectTime = attribution.getLastRejectTime(uidState, uidState, flags); + final long accessDuration = attribution.getLastDuration( + uidState, uidState, flags); // Proxy information for rejections is not backed up - final OpEventProxyInfo proxy = feature.getLastProxyInfo( + final OpEventProxyInfo proxy = attribution.getLastProxyInfo( uidState, uidState, flags); if (accessTime <= 0 && rejectTime <= 0 && accessDuration <= 0 @@ -4284,17 +4250,17 @@ public class AppOpsService extends IAppOpsService.Stub { } String proxyPkg = null; - String proxyFeatureId = null; + String proxyAttributionTag = null; int proxyUid = Process.INVALID_UID; if (proxy != null) { proxyPkg = proxy.getPackageName(); - proxyFeatureId = proxy.getFeatureId(); + proxyAttributionTag = proxy.getAttributionTag(); proxyUid = proxy.getUid(); } out.startTag(null, "st"); - if (featureId != null) { - out.attribute(null, "id", featureId); + if (attributionTag != null) { + out.attribute(null, "id", attributionTag); } out.attribute(null, "n", Long.toString(key)); if (accessTime > 0) { @@ -4309,8 +4275,8 @@ public class AppOpsService extends IAppOpsService.Stub { if (proxyPkg != null) { out.attribute(null, "pp", proxyPkg); } - if (proxyFeatureId != null) { - out.attribute(null, "pc", proxyFeatureId); + if (proxyAttributionTag != null) { + out.attribute(null, "pc", proxyAttributionTag); } if (proxyUid >= 0) { out.attribute(null, "pu", Integer.toString(proxyUid)); @@ -4344,7 +4310,7 @@ public class AppOpsService extends IAppOpsService.Stub { int userId = UserHandle.USER_SYSTEM; String packageName; - String featureId; + String attributionTag; String opStr; String modeStr; int op; @@ -4446,8 +4412,8 @@ public class AppOpsService extends IAppOpsService.Stub { userId = UserHandle.parseUserArg(getNextArgRequired()); } else if ("--uid".equals(argument)) { targetsUid = true; - } else if ("--feature".equals(argument)) { - featureId = getNextArgRequired(); + } else if ("--attribution".equals(argument)) { + attributionTag = getNextArgRequired(); } else { if (packageName == null) { packageName = argument; @@ -4542,13 +4508,16 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println("AppOps service (appops) commands:"); pw.println(" help"); pw.println(" Print this help text."); - pw.println(" start [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> <OP> "); + pw.println(" start [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> " + + "<OP> "); pw.println(" Starts a given operation for a particular application."); - pw.println(" stop [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> <OP> "); + pw.println(" stop [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> " + + "<OP> "); pw.println(" Stops a given operation for a particular application."); pw.println(" set [--user <USER_ID>] <[--uid] PACKAGE | UID> <OP> <MODE>"); pw.println(" Set the mode for a particular application and operation."); - pw.println(" get [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> [<OP>]"); + pw.println(" get [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> " + + "[<OP>]"); pw.println(" Return the mode for a particular application and optional operation."); pw.println(" query-op [--user <USER_ID>] <OP> [<MODE>]"); pw.println(" Print all packages that currently have the given op in the given mode."); @@ -4562,8 +4531,8 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" <PACKAGE> an Android package name or its UID if prefixed by --uid"); pw.println(" <OP> an AppOps operation."); pw.println(" <MODE> one of allow, ignore, deny, or default"); - pw.println(" <USER_ID> the user id under which the package is installed. If --user is not"); - pw.println(" specified, the current user is assumed."); + pw.println(" <USER_ID> the user id under which the package is installed. If --user is"); + pw.println(" not specified, the current user is assumed."); } static int onShellCommand(Shell shell, String cmd) { @@ -4652,7 +4621,7 @@ public class AppOpsService extends IAppOpsService.Stub { pw.print(AppOpsManager.opToName(ent.getOp())); pw.print(": "); pw.print(AppOpsManager.modeToName(ent.getMode())); - if (shell.featureId == null) { + if (shell.attributionTag == null) { if (ent.getLastAccessTime(OP_FLAGS_ALL) != -1) { pw.print("; time="); TimeUtils.formatDuration( @@ -4672,29 +4641,30 @@ public class AppOpsService extends IAppOpsService.Stub { TimeUtils.formatDuration(ent.getLastDuration(OP_FLAGS_ALL), pw); } } else { - final OpFeatureEntry featureEnt = ent.getFeatures().get( - shell.featureId); - if (featureEnt != null) { - if (featureEnt.getLastAccessTime(OP_FLAGS_ALL) != -1) { + final AppOpsManager.AttributedOpEntry attributionEnt = + ent.getAttributedOpEntries().get(shell.attributionTag); + if (attributionEnt != null) { + if (attributionEnt.getLastAccessTime(OP_FLAGS_ALL) != -1) { pw.print("; time="); - TimeUtils.formatDuration(now - featureEnt.getLastAccessTime( - OP_FLAGS_ALL), pw); + TimeUtils.formatDuration( + now - attributionEnt.getLastAccessTime( + OP_FLAGS_ALL), pw); pw.print(" ago"); } - if (featureEnt.getLastRejectTime(OP_FLAGS_ALL) != -1) { + if (attributionEnt.getLastRejectTime(OP_FLAGS_ALL) != -1) { pw.print("; rejectTime="); TimeUtils.formatDuration( - now - featureEnt.getLastRejectTime(OP_FLAGS_ALL), - pw); + now - attributionEnt.getLastRejectTime( + OP_FLAGS_ALL), pw); pw.print(" ago"); } - if (featureEnt.isRunning()) { + if (attributionEnt.isRunning()) { pw.print(" (running)"); - } else if (featureEnt.getLastDuration(OP_FLAGS_ALL) + } else if (attributionEnt.getLastDuration(OP_FLAGS_ALL) != -1) { pw.print("; duration="); TimeUtils.formatDuration( - featureEnt.getLastDuration(OP_FLAGS_ALL), pw); + attributionEnt.getLastDuration(OP_FLAGS_ALL), pw); } } } @@ -4802,7 +4772,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (shell.packageName != null) { shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid, - shell.packageName, shell.featureId, true, true, + shell.packageName, shell.attributionTag, true, true, "appops start shell command"); } else { return -1; @@ -4816,8 +4786,8 @@ public class AppOpsService extends IAppOpsService.Stub { } if (shell.packageName != null) { - shell.mInterface.finishOperation(shell.mToken, - shell.op, shell.packageUid, shell.packageName, shell.featureId); + shell.mInterface.finishOperation(shell.mToken, shell.op, shell.packageUid, + shell.packageName, shell.attributionTag); } else { return -1; } @@ -4842,35 +4812,35 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" Limit output to data associated with the given app op mode."); pw.println(" --package [PACKAGE]"); pw.println(" Limit output to data associated with the given package name."); - pw.println(" --featureId [featureId]"); - pw.println(" Limit output to data associated with the given feature id."); + pw.println(" --attributionTag [attributionTag]"); + pw.println(" Limit output to data associated with the given attribution tag."); pw.println(" --watchers"); pw.println(" Only output the watcher sections."); } - private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterFeatureId, + private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterAttributionTag, @HistoricalOpsRequestFilter int filter, long nowElapsed, @NonNull Op op, long now, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) { - final int numFeatures = op.mFeatures.size(); - for (int i = 0; i < numFeatures; i++) { - if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(op.mFeatures.keyAt(i), - filterFeatureId)) { + final int numAttributions = op.mAttributions.size(); + for (int i = 0; i < numAttributions; i++) { + if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals( + op.mAttributions.keyAt(i), filterAttributionTag)) { continue; } - pw.print(prefix + op.mFeatures.keyAt(i) + "=[\n"); - dumpStatesLocked(pw, nowElapsed, op, op.mFeatures.keyAt(i), now, sdf, date, + pw.print(prefix + op.mAttributions.keyAt(i) + "=[\n"); + dumpStatesLocked(pw, nowElapsed, op, op.mAttributions.keyAt(i), now, sdf, date, prefix + " "); pw.print(prefix + "]\n"); } } private void dumpStatesLocked(@NonNull PrintWriter pw, long nowElapsed, @NonNull Op op, - @Nullable String featureId, long now, @NonNull SimpleDateFormat sdf, + @Nullable String attributionTag, long now, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) { - final OpFeatureEntry entry = op.createSingleFeatureEntryLocked( - featureId).getFeatures().get(featureId); + final AttributedOpEntry entry = op.createSingleAttributionEntryLocked( + attributionTag).getAttributedOpEntries().get(attributionTag); final ArraySet<Long> keys = entry.collectKeys(); @@ -4887,11 +4857,11 @@ public class AppOpsService extends IAppOpsService.Stub { final OpEventProxyInfo proxy = entry.getLastProxyInfo(uidState, uidState, flags); String proxyPkg = null; - String proxyFeatureId = null; + String proxyAttributionTag = null; int proxyUid = Process.INVALID_UID; if (proxy != null) { proxyPkg = proxy.getPackageName(); - proxyFeatureId = proxy.getFeatureId(); + proxyAttributionTag = proxy.getAttributionTag(); proxyUid = proxy.getUid(); } @@ -4915,8 +4885,8 @@ public class AppOpsService extends IAppOpsService.Stub { pw.print(proxyUid); pw.print(", pkg="); pw.print(proxyPkg); - pw.print(", feature="); - pw.print(proxyFeatureId); + pw.print(", attributionTag="); + pw.print(proxyAttributionTag); pw.print("]"); } pw.println(); @@ -4937,21 +4907,21 @@ public class AppOpsService extends IAppOpsService.Stub { pw.print(proxyUid); pw.print(", pkg="); pw.print(proxyPkg); - pw.print(", feature="); - pw.print(proxyFeatureId); + pw.print(", attributionTag="); + pw.print(proxyAttributionTag); pw.print("]"); } pw.println(); } } - final FeatureOp featureOp = op.mFeatures.get(featureId); - if (featureOp.isRunning()) { + final AttributedOp attributedOp = op.mAttributions.get(attributionTag); + if (attributedOp.isRunning()) { long earliestElapsedTime = Long.MAX_VALUE; long maxNumStarts = 0; - int numInProgressEvents = featureOp.mInProgressEvents.size(); + int numInProgressEvents = attributedOp.mInProgressEvents.size(); for (int i = 0; i < numInProgressEvents; i++) { - InProgressStartOpEvent event = featureOp.mInProgressEvents.valueAt(i); + InProgressStartOpEvent event = attributedOp.mInProgressEvents.valueAt(i); earliestElapsedTime = Math.min(earliestElapsedTime, event.getStartElapsedTime()); maxNumStarts = Math.max(maxNumStarts, event.numUnfinishedStarts); @@ -4974,7 +4944,7 @@ public class AppOpsService extends IAppOpsService.Stub { int dumpOp = OP_NONE; String dumpPackage = null; - String dumpFeatureId = null; + String dumpAttributionTag = null; int dumpUid = Process.INVALID_UID; int dumpMode = -1; boolean dumpWatchers = false; @@ -5021,14 +4991,14 @@ public class AppOpsService extends IAppOpsService.Stub { } dumpUid = UserHandle.getAppId(dumpUid); dumpFilter |= FILTER_BY_UID; - } else if ("--featureId".equals(arg)) { + } else if ("--attributionTag".equals(arg)) { i++; if (i >= args.length) { - pw.println("No argument for --featureId option"); + pw.println("No argument for --attributionTag option"); return; } - dumpFeatureId = args[i]; - dumpFilter |= FILTER_BY_FEATURE_ID; + dumpAttributionTag = args[i]; + dumpFilter |= FILTER_BY_ATTRIBUTION_TAG; } else if ("--mode".equals(arg)) { i++; if (i >= args.length) { @@ -5367,8 +5337,8 @@ public class AppOpsService extends IAppOpsService.Stub { pw.print("="); pw.print(AppOpsManager.modeToName(mode)); } pw.println("): "); - dumpStatesLocked(pw, dumpFeatureId, dumpFilter, nowElapsed, op, now, sdf, - date, " "); + dumpStatesLocked(pw, dumpAttributionTag, dumpFilter, nowElapsed, op, now, + sdf, date, " "); } } } @@ -5467,7 +5437,7 @@ public class AppOpsService extends IAppOpsService.Stub { // Must not hold the appops lock if (dumpHistory && !dumpWatchers) { - mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpFeatureId, dumpOp, + mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpAttributionTag, dumpOp, dumpFilter); } } @@ -5572,9 +5542,9 @@ public class AppOpsService extends IAppOpsService.Stub { if (resolvedPackageName == null) { return false; } - // TODO moltmann: Allow to check for feature op activeness + // TODO moltmann: Allow to check for attribution op activeness synchronized (AppOpsService.this) { - Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false, false); + Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false); if (pkgOps == null) { return false; } @@ -5633,7 +5603,7 @@ public class AppOpsService extends IAppOpsService.Stub { * Report runtime access to AppOp together with message (including stack trace) * * @param packageName The package which reported the op - * @param notedAppOp contains code of op and featureId provided by developer + * @param notedAppOp contains code of op and attributionTag provided by developer * @param message Message describing AppOp access (can be stack trace) * * @return Config for future sampling to reduce amount of reporting @@ -5655,7 +5625,7 @@ public class AppOpsService extends IAppOpsService.Stub { reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName, AppOpsManager.strOpToOp(notedAppOp.getOp()), - notedAppOp.getFeatureId(), message); + notedAppOp.getAttributionTag(), message); return new MessageSamplingConfig(mSampledAppOpCode, mAcceptableLeftDistance, Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli()); @@ -5668,17 +5638,18 @@ public class AppOpsService extends IAppOpsService.Stub { * @param uid Uid of the package which reported the op * @param packageName The package which reported the op * @param opCode Code of AppOp - * @param featureId FeautreId of AppOp reported + * @param attributionTag FeautreId of AppOp reported * @param message Message describing AppOp access (can be stack trace) */ private void reportRuntimeAppOpAccessMessageAsyncLocked(int uid, - @NonNull String packageName, int opCode, @Nullable String featureId, + @NonNull String packageName, int opCode, @Nullable String attributionTag, @NonNull String message) { switchPackageIfRarelyUsedLocked(packageName); if (!Objects.equals(mSampledPackage, packageName)) { return; } - reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName, opCode, featureId, message); + reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName, opCode, attributionTag, + message); } /** @@ -5686,7 +5657,7 @@ public class AppOpsService extends IAppOpsService.Stub { * reporting uniformly at random across all received messages. */ private void reportRuntimeAppOpAccessMessageInternalLocked(int uid, - @NonNull String packageName, int opCode, @Nullable String featureId, + @NonNull String packageName, int opCode, @Nullable String attributionTag, @NonNull String message) { int newLeftDistance = AppOpsManager.leftCircularDistance(opCode, mSampledAppOpCode, _NUM_OP); @@ -5703,7 +5674,7 @@ public class AppOpsService extends IAppOpsService.Stub { mMessagesCollectedCount += 1.0f; if (ThreadLocalRandom.current().nextFloat() <= 1.0f / mMessagesCollectedCount) { mCollectedRuntimePermissionMessage = new RuntimeAppOpAccessMessage(uid, opCode, - packageName, featureId, message, mSamplingStrategy); + packageName, attributionTag, message, mSamplingStrategy); } return; } diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index cd450d421b0a..ed4506902f0a 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -15,7 +15,7 @@ */ package com.android.server.appop; -import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID; +import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG; import static android.app.AppOpsManager.FILTER_BY_OP_NAMES; import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME; import static android.app.AppOpsManager.FILTER_BY_UID; @@ -23,7 +23,6 @@ import static android.app.AppOpsManager.FILTER_BY_UID; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; -import android.app.AppOpsManager.HistoricalFeatureOps; import android.app.AppOpsManager.HistoricalMode; import android.app.AppOpsManager.HistoricalOp; import android.app.AppOpsManager.HistoricalOps; @@ -282,7 +281,7 @@ final class HistoricalRegistry { } void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage, - @Nullable String filterFeatureId, int filterOp, + @Nullable String filterAttributionTag, int filterOp, @HistoricalOpsRequestFilter int filter) { if (!isApiEnabled()) { return; @@ -298,7 +297,7 @@ final class HistoricalRegistry { pw.println(AppOpsManager.historicalModeToString(mMode)); final StringDumpVisitor visitor = new StringDumpVisitor(prefix + " ", - pw, filterUid, filterPackage, filterFeatureId, filterOp, filter); + pw, filterUid, filterPackage, filterAttributionTag, filterOp, filter); final long nowMillis = System.currentTimeMillis(); // Dump in memory state first @@ -338,7 +337,7 @@ final class HistoricalRegistry { } void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, - @Nullable String featureId, @Nullable String[] opNames, + @Nullable String attributionTag, @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, @NonNull RemoteCallback callback) { if (!isApiEnabled()) { @@ -354,7 +353,7 @@ final class HistoricalRegistry { return; } final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis); - mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, featureId, + mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, attributionTag, opNames, filter, beginTimeMillis, endTimeMillis, flags); final Bundle payload = new Bundle(); payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result); @@ -363,7 +362,7 @@ final class HistoricalRegistry { } } - void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String featureId, + void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, @NonNull RemoteCallback callback) { @@ -401,7 +400,7 @@ final class HistoricalRegistry { || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) { // Some of the current batch falls into the query, so extract that. final HistoricalOps currentOpsCopy = new HistoricalOps(currentOps); - currentOpsCopy.filter(uid, packageName, featureId, opNames, filter, + currentOpsCopy.filter(uid, packageName, attributionTag, opNames, filter, inMemoryAdjBeginTimeMillis, inMemoryAdjEndTimeMillis); result.merge(currentOpsCopy); } @@ -421,7 +420,7 @@ final class HistoricalRegistry { - onDiskAndInMemoryOffsetMillis, 0); final long onDiskAdjEndTimeMillis = Math.max(inMemoryAdjEndTimeMillis - onDiskAndInMemoryOffsetMillis, 0); - mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, featureId, + mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, attributionTag, opNames, filter, onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis, flags); } @@ -436,7 +435,7 @@ final class HistoricalRegistry { } void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, - @Nullable String featureId, @UidState int uidState, @OpFlags int flags) { + @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { @@ -445,13 +444,13 @@ final class HistoricalRegistry { } getUpdatedPendingHistoricalOpsMLocked( System.currentTimeMillis()).increaseAccessCount(op, uid, packageName, - featureId, uidState, flags, 1); + attributionTag, uidState, flags, 1); } } } void incrementOpRejected(int op, int uid, @NonNull String packageName, - @Nullable String featureId, @UidState int uidState, @OpFlags int flags) { + @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { @@ -460,13 +459,13 @@ final class HistoricalRegistry { } getUpdatedPendingHistoricalOpsMLocked( System.currentTimeMillis()).increaseRejectCount(op, uid, packageName, - featureId, uidState, flags, 1); + attributionTag, uidState, flags, 1); } } } void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, - @Nullable String featureId, @UidState int uidState, @OpFlags int flags, + @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long increment) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { @@ -476,7 +475,7 @@ final class HistoricalRegistry { } getUpdatedPendingHistoricalOpsMLocked( System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName, - featureId, uidState, flags, increment); + attributionTag, uidState, flags, increment); } } } @@ -728,7 +727,7 @@ final class HistoricalRegistry { private static final String TAG_OPS = "ops"; private static final String TAG_UID = "uid"; private static final String TAG_PACKAGE = "pkg"; - private static final String TAG_FEATURE = "ftr"; + private static final String TAG_ATTRIBUTION = "ftr"; private static final String TAG_OP = "op"; private static final String TAG_STATE = "st"; @@ -807,9 +806,9 @@ final class HistoricalRegistry { @Nullable List<HistoricalOps> readHistoryRawDLocked() { return collectHistoricalOpsBaseDLocked(Process.INVALID_UID /*filterUid*/, - null /*filterPackageName*/, null /*filterFeatureId*/, null /*filterOpNames*/, - 0 /*filter*/, 0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/, - AppOpsManager.OP_FLAGS_ALL); + null /*filterPackageName*/, null /*filterAttributionTag*/, + null /*filterOpNames*/, 0 /*filter*/, 0 /*filterBeginTimeMills*/, + Long.MAX_VALUE /*filterEndTimeMills*/, AppOpsManager.OP_FLAGS_ALL); } @Nullable List<HistoricalOps> readHistoryDLocked() { @@ -861,13 +860,13 @@ final class HistoricalRegistry { return 0; } - private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps, - int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId, + private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps, int filterUid, + @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, long filterBeingMillis, long filterEndMillis, @OpFlags int filterFlags) { final List<HistoricalOps> readOps = collectHistoricalOpsBaseDLocked(filterUid, - filterPackageName, filterFeatureId, filterOpNames, filter, filterBeingMillis, - filterEndMillis, filterFlags); + filterPackageName, filterAttributionTag, filterOpNames, filter, + filterBeingMillis, filterEndMillis, filterFlags); if (readOps != null) { final int readCount = readOps.size(); for (int i = 0; i < readCount; i++) { @@ -877,8 +876,8 @@ final class HistoricalRegistry { } } - private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked( - int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId, + private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(int filterUid, + @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) { File baseDir = null; @@ -892,7 +891,7 @@ final class HistoricalRegistry { final Set<String> historyFiles = getHistoricalFileNames(baseDir); final long[] globalContentOffsetMillis = {0}; final LinkedList<HistoricalOps> ops = collectHistoricalOpsRecursiveDLocked( - baseDir, filterUid, filterPackageName, filterFeatureId, filterOpNames, + baseDir, filterUid, filterPackageName, filterAttributionTag, filterOpNames, filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags, globalContentOffsetMillis, null /*outOps*/, 0 /*depth*/, historyFiles); if (DEBUG) { @@ -909,7 +908,7 @@ final class HistoricalRegistry { private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsRecursiveDLocked( @NonNull File baseDir, int filterUid, @Nullable String filterPackageName, - @Nullable String filterFeatureId, @Nullable String[] filterOpNames, + @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags, @NonNull long[] globalContentOffsetMillis, @@ -927,7 +926,7 @@ final class HistoricalRegistry { // Read historical data at this level final List<HistoricalOps> readOps = readHistoricalOpsLocked(baseDir, previousIntervalEndMillis, currentIntervalEndMillis, filterUid, - filterPackageName, filterFeatureId, filterOpNames, filter, + filterPackageName, filterAttributionTag, filterOpNames, filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags, globalContentOffsetMillis, depth, historyFiles); // Empty is a special signal to stop diving @@ -937,7 +936,7 @@ final class HistoricalRegistry { // Collect older historical data from subsequent levels outOps = collectHistoricalOpsRecursiveDLocked(baseDir, filterUid, filterPackageName, - filterFeatureId, filterOpNames, filter, filterBeginTimeMillis, + filterAttributionTag, filterOpNames, filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags, globalContentOffsetMillis, outOps, depth + 1, historyFiles); @@ -1006,7 +1005,7 @@ final class HistoricalRegistry { final List<HistoricalOps> existingOps = readHistoricalOpsLocked(oldBaseDir, previousIntervalEndMillis, currentIntervalEndMillis, Process.INVALID_UID /*filterUid*/, null /*filterPackageName*/, - null /*filterFeatureId*/, null /*filterOpNames*/, 0 /*filter*/, + null /*filterAttributionTag*/, null /*filterOpNames*/, 0 /*filter*/, Long.MIN_VALUE /*filterBeginTimeMillis*/, Long.MAX_VALUE /*filterEndTimeMillis*/, AppOpsManager.OP_FLAGS_ALL, null, depth, null /*historyFiles*/); @@ -1120,7 +1119,7 @@ final class HistoricalRegistry { private @Nullable List<HistoricalOps> readHistoricalOpsLocked(File baseDir, long intervalBeginMillis, long intervalEndMillis, int filterUid, - @Nullable String filterPackageName, @Nullable String filterFeatureId, + @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags, @Nullable long[] cumulativeOverflowMillis, int depth, @@ -1147,15 +1146,16 @@ final class HistoricalRegistry { return null; } } - return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterFeatureId, + return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterAttributionTag, filterOpNames, filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags, cumulativeOverflowMillis); } - private @Nullable List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file, - int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId, - @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, - long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags, + private @Nullable List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file, + int filterUid, @Nullable String filterPackageName, + @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, + @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis, + long filterEndTimeMillis, @OpFlags int filterFlags, @Nullable long[] cumulativeOverflowMillis) throws IOException, XmlPullParserException { if (DEBUG) { @@ -1180,7 +1180,7 @@ final class HistoricalRegistry { while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_OPS.equals(parser.getName())) { final HistoricalOps ops = readeHistoricalOpsDLocked(parser, filterUid, - filterPackageName, filterFeatureId, filterOpNames, filter, + filterPackageName, filterAttributionTag, filterOpNames, filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags, cumulativeOverflowMillis); if (ops == null) { @@ -1215,7 +1215,7 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readeHistoricalOpsDLocked( @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName, - @Nullable String filterFeatureId, @Nullable String[] filterOpNames, + @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags, @Nullable long[] cumulativeOverflowMillis) @@ -1245,8 +1245,8 @@ final class HistoricalRegistry { while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_UID.equals(parser.getName())) { final HistoricalOps returnedOps = readHistoricalUidOpsDLocked(ops, parser, - filterUid, filterPackageName, filterFeatureId, filterOpNames, filter, - filterFlags, filterScale); + filterUid, filterPackageName, filterAttributionTag, filterOpNames, + filter, filterFlags, filterScale); if (ops == null) { ops = returnedOps; } @@ -1260,7 +1260,7 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readHistoricalUidOpsDLocked( @Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid, - @Nullable String filterPackageName, @Nullable String filterFeatureId, + @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) throws IOException, XmlPullParserException { @@ -1272,8 +1272,8 @@ final class HistoricalRegistry { final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_PACKAGE.equals(parser.getName())) { - final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops, - uid, parser, filterPackageName, filterFeatureId, filterOpNames, filter, + final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops, uid, + parser, filterPackageName, filterAttributionTag, filterOpNames, filter, filterFlags, filterScale); if (ops == null) { ops = returnedOps; @@ -1285,7 +1285,7 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readHistoricalPackageOpsDLocked( @Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser, - @Nullable String filterPackageName, @Nullable String filterFeatureId, + @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) throws IOException, XmlPullParserException { @@ -1296,9 +1296,9 @@ final class HistoricalRegistry { } final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { - if (TAG_FEATURE.equals(parser.getName())) { - final HistoricalOps returnedOps = readHistoricalFeatureOpsDLocked(ops, uid, - packageName, parser, filterFeatureId, filterOpNames, filter, + if (TAG_ATTRIBUTION.equals(parser.getName())) { + final HistoricalOps returnedOps = readHistoricalAttributionOpsDLocked(ops, uid, + packageName, parser, filterAttributionTag, filterOpNames, filter, filterFlags, filterScale); if (ops == null) { ops = returnedOps; @@ -1308,15 +1308,15 @@ final class HistoricalRegistry { return ops; } - private @Nullable HistoricalOps readHistoricalFeatureOpsDLocked(@Nullable HistoricalOps ops, - int uid, String packageName, @NonNull XmlPullParser parser, - @Nullable String filterFeatureId, @Nullable String[] filterOpNames, - @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, - double filterScale) + private @Nullable HistoricalOps readHistoricalAttributionOpsDLocked( + @Nullable HistoricalOps ops, int uid, String packageName, + @NonNull XmlPullParser parser, @Nullable String filterAttributionTag, + @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, + @OpFlags int filterFlags, double filterScale) throws IOException, XmlPullParserException { - final String featureId = XmlUtils.readStringAttribute(parser, ATTR_NAME); - if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(filterFeatureId, - featureId)) { + final String attributionTag = XmlUtils.readStringAttribute(parser, ATTR_NAME); + if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(filterAttributionTag, + attributionTag)) { XmlUtils.skipCurrentTag(parser); return null; } @@ -1324,7 +1324,8 @@ final class HistoricalRegistry { while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_OP.equals(parser.getName())) { final HistoricalOps returnedOps = readHistoricalOpDLocked(ops, uid, packageName, - featureId, parser, filterOpNames, filter, filterFlags, filterScale); + attributionTag, parser, filterOpNames, filter, filterFlags, + filterScale); if (ops == null) { ops = returnedOps; } @@ -1334,7 +1335,7 @@ final class HistoricalRegistry { } private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops, - int uid, @NonNull String packageName, @Nullable String featureId, + int uid, @NonNull String packageName, @Nullable String attributionTag, @NonNull XmlPullParser parser, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) @@ -1349,7 +1350,7 @@ final class HistoricalRegistry { while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_STATE.equals(parser.getName())) { final HistoricalOps returnedOps = readStateDLocked(ops, uid, - packageName, featureId, op, parser, filterFlags, filterScale); + packageName, attributionTag, op, parser, filterFlags, filterScale); if (ops == null) { ops = returnedOps; } @@ -1359,7 +1360,7 @@ final class HistoricalRegistry { } private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops, - int uid, @NonNull String packageName, @Nullable String featureId, int op, + int uid, @NonNull String packageName, @Nullable String attributionTag, int op, @NonNull XmlPullParser parser, @OpFlags int filterFlags, double filterScale) throws IOException { final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME); @@ -1377,7 +1378,7 @@ final class HistoricalRegistry { if (ops == null) { ops = new HistoricalOps(0, 0); } - ops.increaseAccessCount(op, uid, packageName, featureId, uidState, flags, + ops.increaseAccessCount(op, uid, packageName, attributionTag, uidState, flags, accessCount); } long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0); @@ -1389,7 +1390,7 @@ final class HistoricalRegistry { if (ops == null) { ops = new HistoricalOps(0, 0); } - ops.increaseRejectCount(op, uid, packageName, featureId, uidState, flags, + ops.increaseRejectCount(op, uid, packageName, attributionTag, uidState, flags, rejectCount); } long accessDuration = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0); @@ -1401,7 +1402,7 @@ final class HistoricalRegistry { if (ops == null) { ops = new HistoricalOps(0, 0); } - ops.increaseAccessDuration(op, uid, packageName, featureId, uidState, flags, + ops.increaseAccessDuration(op, uid, packageName, attributionTag, uidState, flags, accessDuration); } return ops; @@ -1467,24 +1468,25 @@ final class HistoricalRegistry { @NonNull XmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_PACKAGE); serializer.attribute(null, ATTR_NAME, packageOps.getPackageName()); - final int numFeatures = packageOps.getFeatureCount(); - for (int i = 0; i < numFeatures; i++) { - final HistoricalFeatureOps op = packageOps.getFeatureOpsAt(i); - writeHistoricalFeatureOpsDLocked(op, serializer); + final int numAttributions = packageOps.getAttributedOpsCount(); + for (int i = 0; i < numAttributions; i++) { + final AppOpsManager.AttributedHistoricalOps op = packageOps.getAttributedOpsAt(i); + writeHistoricalAttributionOpsDLocked(op, serializer); } serializer.endTag(null, TAG_PACKAGE); } - private void writeHistoricalFeatureOpsDLocked(@NonNull HistoricalFeatureOps featureOps, + private void writeHistoricalAttributionOpsDLocked( + @NonNull AppOpsManager.AttributedHistoricalOps attributionOps, @NonNull XmlSerializer serializer) throws IOException { - serializer.startTag(null, TAG_FEATURE); - XmlUtils.writeStringAttribute(serializer, ATTR_NAME, featureOps.getFeatureId()); - final int opCount = featureOps.getOpCount(); + serializer.startTag(null, TAG_ATTRIBUTION); + XmlUtils.writeStringAttribute(serializer, ATTR_NAME, attributionOps.getTag()); + final int opCount = attributionOps.getOpCount(); for (int i = 0; i < opCount; i++) { - final HistoricalOp op = featureOps.getOpAt(i); + final HistoricalOp op = attributionOps.getOpAt(i); writeHistoricalOpDLocked(op, serializer); } - serializer.endTag(null, TAG_FEATURE); + serializer.endTag(null, TAG_ATTRIBUTION); } private void writeHistoricalOpDLocked(@NonNull HistoricalOp op, @@ -1718,29 +1720,29 @@ final class HistoricalRegistry { private final @NonNull String mOpsPrefix; private final @NonNull String mUidPrefix; private final @NonNull String mPackagePrefix; - private final @NonNull String mFeaturePrefix; + private final @NonNull String mAttributionPrefix; private final @NonNull String mEntryPrefix; private final @NonNull String mUidStatePrefix; private final @NonNull PrintWriter mWriter; private final int mFilterUid; private final String mFilterPackage; - private final String mFilterFeatureId; + private final String mFilterAttributionTag; private final int mFilterOp; private final @HistoricalOpsRequestFilter int mFilter; StringDumpVisitor(@NonNull String prefix, @NonNull PrintWriter writer, int filterUid, - @Nullable String filterPackage, @Nullable String filterFeatureId, int filterOp, + @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp, @HistoricalOpsRequestFilter int filter) { mOpsPrefix = prefix + " "; mUidPrefix = mOpsPrefix + " "; mPackagePrefix = mUidPrefix + " "; - mFeaturePrefix = mPackagePrefix + " "; - mEntryPrefix = mFeaturePrefix + " "; + mAttributionPrefix = mPackagePrefix + " "; + mEntryPrefix = mAttributionPrefix + " "; mUidStatePrefix = mEntryPrefix + " "; mWriter = writer; mFilterUid = filterUid; mFilterPackage = filterPackage; - mFilterFeatureId = filterFeatureId; + mFilterAttributionTag = filterAttributionTag; mFilterOp = filterOp; mFilter = filter; } @@ -1791,14 +1793,14 @@ final class HistoricalRegistry { } @Override - public void visitHistoricalFeatureOps(HistoricalFeatureOps ops) { - if ((mFilter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(mFilterPackage, - ops.getFeatureId())) { + public void visitHistoricalAttributionOps(AppOpsManager.AttributedHistoricalOps ops) { + if ((mFilter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(mFilterPackage, + ops.getTag())) { return; } - mWriter.print(mFeaturePrefix); - mWriter.print("Feature "); - mWriter.print(ops.getFeatureId()); + mWriter.print(mAttributionPrefix); + mWriter.print("Attribution "); + mWriter.print(ops.getTag()); mWriter.println(":"); } diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index 7bdeb5969f1f..2e9818d15963 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -151,6 +151,15 @@ public final class CompatChange extends CompatibilityChangeInfo { return true; } + /** + * Checks whether a change has an override for a package. + * @param packageName name of the package + * @return true if there is such override + */ + boolean hasOverride(String packageName) { + return mPackageOverrides != null && mPackageOverrides.containsKey(packageName); + } + @Override public String toString() { StringBuilder sb = new StringBuilder("ChangeId(") diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 61bede987bae..aeaa1fedf9e3 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -247,11 +247,13 @@ final class CompatConfig { CompatChange c = mChanges.get(changeId); try { if (c != null) { - OverrideAllowedState allowedState = - mOverrideValidator.getOverrideAllowedState(changeId, packageName); - allowedState.enforce(changeId, packageName); - overrideExists = true; - c.removePackageOverride(packageName); + overrideExists = c.hasOverride(packageName); + if (overrideExists) { + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(changeId, packageName); + allowedState.enforce(changeId, packageName); + c.removePackageOverride(packageName); + } } } catch (RemoteException e) { // Should never occur, since validator is in the same process. @@ -298,20 +300,79 @@ final class CompatConfig { for (int i = 0; i < mChanges.size(); ++i) { try { CompatChange change = mChanges.valueAt(i); + if (change.hasOverride(packageName)) { + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(change.getId(), + packageName); + allowedState.enforce(change.getId(), packageName); + if (change != null) { + mChanges.valueAt(i).removePackageOverride(packageName); + } + } + } catch (RemoteException e) { + // Should never occur, since validator is in the same process. + throw new RuntimeException("Unable to call override validator!", e); + } + } + invalidateCache(); + } + } + + private long[] getAllowedChangesAfterTargetSdkForPackage(String packageName, + int targetSdkVersion) + throws RemoteException { + LongArray allowed = new LongArray(); + synchronized (mChanges) { + for (int i = 0; i < mChanges.size(); ++i) { + try { + CompatChange change = mChanges.valueAt(i); + if (change.getEnableAfterTargetSdk() != targetSdkVersion) { + continue; + } OverrideAllowedState allowedState = mOverrideValidator.getOverrideAllowedState(change.getId(), packageName); - allowedState.enforce(change.getId(), packageName); - if (change != null) { - mChanges.valueAt(i).removePackageOverride(packageName); + if (allowedState.state == OverrideAllowedState.ALLOWED) { + allowed.add(change.getId()); } } catch (RemoteException e) { // Should never occur, since validator is in the same process. throw new RuntimeException("Unable to call override validator!", e); } } - invalidateCache(); } + return allowed.toArray(); + } + + /** + * Enables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for + * {@param packageName}. + * + * @return The number of changes that were toggled. + */ + int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) + throws RemoteException { + long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion); + for (long changeId : changes) { + addOverride(changeId, packageName, true); + } + return changes.length; + } + + + /** + * Disables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for + * {@param packageName}. + * + * @return The number of changes that were toggled. + */ + int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) + throws RemoteException { + long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion); + for (long changeId : changes) { + addOverride(changeId, packageName, false); + } + return changes.length; } boolean registerListener(long changeId, CompatChange.ChangeListener listener) { diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 821653af301d..8519b00237aa 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -166,6 +166,26 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + public int enableTargetSdkChanges(String packageName, int targetSdkVersion) + throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); + int numChanges = mCompatConfig.enableTargetSdkChangesForPackage(packageName, + targetSdkVersion); + killPackage(packageName); + return numChanges; + } + + @Override + public int disableTargetSdkChanges(String packageName, int targetSdkVersion) + throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); + int numChanges = mCompatConfig.disableTargetSdkChangesForPackage(packageName, + targetSdkVersion); + killPackage(packageName); + return numChanges; + } + + @Override public void clearOverrides(String packageName) throws RemoteException, SecurityException { checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 7c3cab17704f..20ffd9f51d6e 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -2563,7 +2563,7 @@ public class Vpn { public void exitIfOuterInterfaceIs(String interfaze) { if (interfaze.equals(mOuterInterface)) { Log.i(TAG, "Legacy VPN is going down with " + interfaze); - exit(); + exitVpnRunner(); } } @@ -2572,6 +2572,10 @@ public class Vpn { public void exitVpnRunner() { // We assume that everything is reset after stopping the daemons. interrupt(); + + // Always disconnect. This may be called again in cleanupVpnStateLocked() if + // exitVpnRunner() was called from exit(), but it will be a no-op. + agentDisconnect(); try { mContext.unregisterReceiver(mBroadcastReceiver); } catch (IllegalArgumentException e) {} @@ -2794,7 +2798,7 @@ public class Vpn { } catch (Exception e) { Log.i(TAG, "Aborting", e); updateState(DetailedState.FAILED, e.getMessage()); - exit(); + exitVpnRunner(); } } diff --git a/services/core/java/com/android/server/location/CountryDetectorBase.java b/services/core/java/com/android/server/location/CountryDetectorBase.java index b158388281d8..682b104f6d7a 100644 --- a/services/core/java/com/android/server/location/CountryDetectorBase.java +++ b/services/core/java/com/android/server/location/CountryDetectorBase.java @@ -31,7 +31,7 @@ import android.os.Handler; * @hide */ public abstract class CountryDetectorBase { - private static final String FEATURE_ID = "CountryDetector"; + private static final String ATTRIBUTION_TAG = "CountryDetector"; protected final Handler mHandler; protected final Context mContext; @@ -39,7 +39,7 @@ public abstract class CountryDetectorBase { protected Country mDetectedCountry; public CountryDetectorBase(Context context) { - mContext = context.createFeatureContext(FEATURE_ID); + mContext = context.createAttributionContext(ATTRIBUTION_TAG); mHandler = new Handler(); } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index b456737e9fc1..2aa53cc3882e 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -60,6 +60,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private Connection mActiveConnection; private boolean mConnectionReady; + private RouteDiscoveryPreference mPendingDiscoveryPreference = null; + MediaRoute2ProviderServiceProxy(@NonNull Context context, @NonNull ComponentName componentName, int userId) { super(componentName); @@ -99,6 +101,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider if (mConnectionReady) { mActiveConnection.updateDiscoveryPreference(discoveryPreference); updateBinding(); + } else { + mPendingDiscoveryPreference = discoveryPreference; } } @@ -271,6 +275,10 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private void onConnectionReady(Connection connection) { if (mActiveConnection == connection) { mConnectionReady = true; + if (mPendingDiscoveryPreference != null) { + updateDiscoveryPreference(mPendingDiscoveryPreference); + mPendingDiscoveryPreference = null; + } } } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index ee5a4fe248e7..4af31b036940 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -306,9 +306,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** Data layer operation counters for splicing into other structures. */ private NetworkStats mUidOperations = new NetworkStats(0L, 10); - /** Must be set in factory by calling #setHandler. */ - private Handler mHandler; - private Handler.Callback mHandlerCallback; + @NonNull + private final Handler mHandler; private volatile boolean mSystemReady; private long mPersistThreshold = 2 * MB_IN_BYTES; @@ -324,6 +323,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final static int DUMP_STATS_SESSION_COUNT = 20; + @NonNull + private final Dependencies mDeps; + private static @NonNull File getDefaultSystemDir() { return new File(Environment.getDataDirectory(), "system"); } @@ -339,9 +341,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub { Clock.systemUTC()); } - private static final class NetworkStatsHandler extends Handler { - NetworkStatsHandler(Looper looper, Handler.Callback callback) { - super(looper, callback); + private final class NetworkStatsHandler extends Handler { + NetworkStatsHandler(@NonNull Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_PERFORM_POLL: { + performPoll(FLAG_PERSIST_ALL); + break; + } + case MSG_PERFORM_POLL_REGISTER_ALERT: { + performPoll(FLAG_PERSIST_NETWORK); + registerGlobalAlert(); + break; + } + } } } @@ -355,14 +372,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { NetworkStatsService service = new NetworkStatsService(context, networkManager, alarmManager, wakeLock, getDefaultClock(), context.getSystemService(TelephonyManager.class), new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(), - new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir()); + new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(), + new Dependencies()); service.registerLocalService(); - HandlerThread handlerThread = new HandlerThread(TAG); - Handler.Callback callback = new HandlerCallback(service); - handlerThread.start(); - Handler handler = new NetworkStatsHandler(handlerThread.getLooper(), callback); - service.setHandler(handler, callback); return service; } @@ -373,7 +386,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock, TelephonyManager teleManager, NetworkStatsSettings settings, NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir, - File baseDir) { + File baseDir, @NonNull Dependencies deps) { mContext = Objects.requireNonNull(context, "missing Context"); mNetworkManager = Objects.requireNonNull(networkManager, "missing INetworkManagementService"); @@ -387,6 +400,26 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mSystemDir = Objects.requireNonNull(systemDir, "missing systemDir"); mBaseDir = Objects.requireNonNull(baseDir, "missing baseDir"); mUseBpfTrafficStats = new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists(); + mDeps = Objects.requireNonNull(deps, "missing Dependencies"); + + final HandlerThread handlerThread = mDeps.makeHandlerThread(); + handlerThread.start(); + mHandler = new NetworkStatsHandler(handlerThread.getLooper()); + } + + /** + * Dependencies of NetworkStatsService, for injection in tests. + */ + // TODO: Move more stuff into dependencies object. + @VisibleForTesting + public static class Dependencies { + /** + * Create a HandlerThread to use in NetworkStatsService. + */ + @NonNull + public HandlerThread makeHandlerThread() { + return new HandlerThread(TAG); + } } private void registerLocalService() { @@ -394,12 +427,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { new NetworkStatsManagerInternalImpl()); } - @VisibleForTesting - void setHandler(Handler handler, Handler.Callback callback) { - mHandler = handler; - mHandlerCallback = callback; - } - public void systemReady() { synchronized (mStatsLock) { mSystemReady = true; @@ -1920,33 +1947,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } - @VisibleForTesting - static class HandlerCallback implements Handler.Callback { - private final NetworkStatsService mService; - - HandlerCallback(NetworkStatsService service) { - this.mService = service; - } - - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case MSG_PERFORM_POLL: { - mService.performPoll(FLAG_PERSIST_ALL); - return true; - } - case MSG_PERFORM_POLL_REGISTER_ALERT: { - mService.performPoll(FLAG_PERSIST_NETWORK); - mService.registerGlobalAlert(); - return true; - } - default: { - return false; - } - } - } - } - private void assertSystemReady() { if (!mSystemReady) { throw new IllegalStateException("System not ready"); diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index ec9b37db3137..83da38195053 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -45,6 +45,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -183,7 +184,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { String callingFeatureId, Intent intent, @UserIdInt int userId, - IBinder callingActivity) throws RemoteException { + IBinder callingActivity, + Bundle options) throws RemoteException { Objects.requireNonNull(callingPackage); Objects.requireNonNull(intent); Objects.requireNonNull(intent.getComponent(), "The intent must have a Component set"); @@ -226,7 +228,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { launchIntent, callingActivity, /* startFlags= */ 0, - /* options= */ null, + options, userId); logStartActivityByIntent(callingPackage); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 33ef2d43d720..cdc37364ca79 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -805,26 +805,30 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public SessionInfo getSessionInfo(int sessionId) { synchronized (mSessions) { final PackageInstallerSession session = mSessions.get(sessionId); - return session != null ? session.generateInfo() : null; + + return session != null + ? session.generateInfoForCaller(true /*withIcon*/, Binder.getCallingUid()) + : null; } } @Override public ParceledListSlice<SessionInfo> getStagedSessions() { - return mStagingManager.getSessions(); + return mStagingManager.getSessions(Binder.getCallingUid()); } @Override public ParceledListSlice<SessionInfo> getAllSessions(int userId) { + final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission( - Binder.getCallingUid(), userId, true, false, "getAllSessions"); + callingUid, userId, true, false, "getAllSessions"); final List<SessionInfo> result = new ArrayList<>(); synchronized (mSessions) { for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); if (session.userId == userId && !session.hasParentSessionId()) { - result.add(session.generateInfo(false)); + result.add(session.generateInfoForCaller(false, callingUid)); } } } @@ -842,7 +846,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); - SessionInfo info = session.generateInfo(false); + SessionInfo info = + session.generateInfoForCaller(false /*withIcon*/, Process.SYSTEM_UID); if (Objects.equals(info.getInstallerPackageName(), installerPackageName) && session.userId == userId && !session.hasParentSessionId()) { result.add(info); @@ -1302,7 +1307,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements session.markUpdated(); writeSessionsAsync(); if (mOkToSendBroadcasts) { - mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), + // we don't scrub the data here as this is sent only to the installer several + // privileged system packages + mPm.sendSessionUpdatedBroadcast( + session.generateInfoForCaller(false/*icon*/, Process.SYSTEM_UID), session.userId); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 97aa79de5e8d..483f83efd508 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -557,11 +557,41 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - public SessionInfo generateInfo() { - return generateInfo(true); + /** + * Returns {@code true} if the {@link SessionInfo} object should be produced with potentially + * sensitive data scrubbed from its fields. + * + * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may + * need to be scrubbed + */ + private boolean shouldScrubData(int callingUid) { + return !(callingUid < Process.FIRST_APPLICATION_UID || getInstallerUid() == callingUid); + } + + /** + * Generates a {@link SessionInfo} object for the provided uid. This may result in some fields + * that may contain sensitive info being filtered. + * + * @param includeIcon true if the icon should be included in the object + * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may + * need to be scrubbed + * @see #shouldScrubData(int) + */ + public SessionInfo generateInfoForCaller(boolean includeIcon, int callingUid) { + return generateInfoInternal(includeIcon, shouldScrubData(callingUid)); } - public SessionInfo generateInfo(boolean includeIcon) { + /** + * Generates a {@link SessionInfo} object to ensure proper hiding of sensitive fields. + * + * @param includeIcon true if the icon should be included in the object + * @see #generateInfoForCaller(boolean, int) + */ + public SessionInfo generateInfoScrubbed(boolean includeIcon) { + return generateInfoInternal(includeIcon, true /*scrubData*/); + } + + private SessionInfo generateInfoInternal(boolean includeIcon, boolean scrubData) { final SessionInfo info = new SessionInfo(); synchronized (mLock) { info.sessionId = sessionId; @@ -584,9 +614,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { info.appLabel = params.appLabel; info.installLocation = params.installLocation; - info.originatingUri = params.originatingUri; + if (!scrubData) { + info.originatingUri = params.originatingUri; + } info.originatingUid = params.originatingUid; - info.referrerUri = params.referrerUri; + if (!scrubData) { + info.referrerUri = params.referrerUri; + } info.grantedRuntimePermissions = params.grantedRuntimePermissions; info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions; info.installFlags = params.installFlags; @@ -2664,7 +2698,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts() && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mPm.sendSessionCommitBroadcast(generateInfo(), userId); + mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId); } mCallback.onSessionFinished(this, success); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 799ce65669db..61bf5f0b6dfd 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -11514,8 +11514,8 @@ public class PackageManagerService extends IPackageManager.Stub "Static shared libs cannot declare permission groups"); } - // Static shared libs cannot declare features - if (!pkg.getFeatures().isEmpty()) { + // Static shared libs cannot declare attributions + if (!pkg.getAttributions().isEmpty()) { throw new PackageManagerException( "Static shared libs cannot declare features"); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 2d16854f787a..62541ab4951e 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -5450,7 +5450,7 @@ public final class Settings { packagePermissions, sharedUserPermissions); } - mPersistence.writeAsUser(runtimePermissions, UserHandle.of(userId)); + mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId)); } @NonNull @@ -5504,12 +5504,12 @@ public final class Settings { } public void deleteUserRuntimePermissionsFile(int userId) { - mPersistence.deleteAsUser(UserHandle.of(userId)); + mPersistence.deleteForUser(UserHandle.of(userId)); } @GuardedBy("Settings.this.mLock") public void readStateForUserSyncLPr(int userId) { - RuntimePermissionsState runtimePermissions = mPersistence.readAsUser(UserHandle.of( + RuntimePermissionsState runtimePermissions = mPersistence.readForUser(UserHandle.of( userId)); if (runtimePermissions == null) { readLegacyStateForUserSyncLPr(userId); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 83fe5562324f..342c90719916 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -128,11 +128,12 @@ public class StagingManager { } } - ParceledListSlice<PackageInstaller.SessionInfo> getSessions() { + ParceledListSlice<PackageInstaller.SessionInfo> getSessions(int callingUid) { final List<PackageInstaller.SessionInfo> result = new ArrayList<>(); synchronized (mStagedSessions) { for (int i = 0; i < mStagedSessions.size(); i++) { - result.add(mStagedSessions.valueAt(i).generateInfo(false)); + final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i); + result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid)); } } return new ParceledListSlice<>(result); diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index d3f668c5f4d3..0e294f707985 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -391,8 +391,9 @@ public class PackageInfoUtils { ArrayMap<String, ProcessInfo> retProcs = new ArrayMap<>(numProcs); for (String key : procs.keySet()) { ParsedProcess proc = procs.get(key); - retProcs.put(proc.getName(), new ProcessInfo(proc.getName(), - new ArraySet<>(proc.getDeniedPermissions()))); + retProcs.put(proc.getName(), + new ProcessInfo(proc.getName(), new ArraySet<>(proc.getDeniedPermissions()), + proc.getEnableGwpAsan())); } return retProcs; } diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java index 792957935870..46b08df1a52e 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java @@ -25,7 +25,7 @@ import android.content.pm.PackageParser; import android.content.pm.PermissionGroupInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.parsing.ParsingPackageRead; -import android.content.pm.parsing.component.ParsedFeature; +import android.content.pm.parsing.component.ParsedAttribution; import android.content.pm.parsing.component.ParsedIntentInfo; import android.content.pm.parsing.component.ParsedPermissionGroup; import android.os.Bundle; @@ -147,7 +147,7 @@ public interface AndroidPackage extends PkgAppInfo, PkgPackageInfo, ParsingPacka List<ParsedPermissionGroup> getPermissionGroups(); @NonNull - List<ParsedFeature> getFeatures(); + List<ParsedAttribution> getAttributions(); /** * Used to determine the default preferred handler of an {@link Intent}. diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index d589353cf3a0..161f30449a52 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -32,6 +32,7 @@ import android.app.AppOpsManager; import android.app.AppOpsManagerInternal; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -173,6 +174,65 @@ public final class PermissionPolicyService extends SystemService { } catch (RemoteException doesNotHappen) { Slog.wtf(LOG_TAG, "Cannot set up app-ops listener"); } + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); + intentFilter.addDataScheme("package"); + + + /* TODO ntmyren: enable receiver when test flakes are fixed + getContext().registerReceiverAsUser(new BroadcastReceiver() { + final List<Integer> mUserSetupUids = new ArrayList<>(200); + final Map<UserHandle, PermissionControllerManager> mPermControllerManagers = + new HashMap<>(); + + @Override + public void onReceive(Context context, Intent intent) { + boolean hasSetupRun = true; + try { + hasSetupRun = Settings.Secure.getInt(getContext().getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE) != 0; + } catch (Settings.SettingNotFoundException e) { + // Ignore error, assume setup has run + } + int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + // If there is no valid package for the given UID, return immediately + if (packageManagerInternal.getPackage(uid) == null) { + return; + } + + if (hasSetupRun) { + if (!mUserSetupUids.isEmpty()) { + synchronized (mUserSetupUids) { + for (int i = mUserSetupUids.size() - 1; i >= 0; i--) { + updateUid(mUserSetupUids.get(i)); + } + mUserSetupUids.clear(); + } + } + updateUid(uid); + } else { + synchronized (mUserSetupUids) { + if (!mUserSetupUids.contains(uid)) { + mUserSetupUids.add(uid); + } + } + } + } + + private void updateUid(int uid) { + UserHandle user = UserHandle.getUserHandleForUid(uid); + PermissionControllerManager manager = mPermControllerManagers.get(user); + if (manager == null) { + manager = new PermissionControllerManager( + getUserContext(getContext(), user), FgThread.getHandler()); + mPermControllerManagers.put(user, manager); + } + manager.updateUserSensitiveForApp(uid); + } + }, UserHandle.ALL, intentFilter, null, null); + */ } /** @@ -182,7 +242,6 @@ public final class PermissionPolicyService extends SystemService { * {@link AppOpsManager#sOpToSwitch share an op} to control the access. * * @param permission The permission - * * @return The op that controls the access of the permission */ private static int getSwitchOp(@NonNull String permission) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 1b5cc6a248e3..64edacd438fc 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -5202,7 +5202,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (dock != null) { int result = ActivityTaskManager.getService() .startActivityAsUser(null, mContext.getBasePackageName(), - mContext.getFeatureId(), dock, + mContext.getAttributionTag(), dock, dock.resolveTypeIfNeeded(mContext.getContentResolver()), null, null, 0, ActivityManager.START_FLAG_ONLY_IF_NEEDED, @@ -5214,7 +5214,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } int result = ActivityTaskManager.getService() .startActivityAsUser(null, mContext.getBasePackageName(), - mContext.getFeatureId(), mHomeIntent, + mContext.getAttributionTag(), mHomeIntent, mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()), null, null, 0, ActivityManager.START_FLAG_ONLY_IF_NEEDED, diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index 97ce6bd7f369..b33dc8fab9a5 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -364,12 +364,12 @@ public class RoleUserState { (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked()); } - mPersistence.writeAsUser(roles, UserHandle.of(mUserId)); + mPersistence.writeForUser(roles, UserHandle.of(mUserId)); } private void readFile() { synchronized (mLock) { - RolesState roles = mPersistence.readAsUser(UserHandle.of(mUserId)); + RolesState roles = mPersistence.readForUser(UserHandle.of(mUserId)); if (roles == null) { readLegacyFileLocked(); scheduleWriteFileLocked(); @@ -545,7 +545,7 @@ public class RoleUserState { throw new IllegalStateException("This RoleUserState has already been destroyed"); } mWriteHandler.removeCallbacksAndMessages(null); - mPersistence.deleteAsUser(UserHandle.of(mUserId)); + mPersistence.deleteForUser(UserHandle.of(mUserId)); mDestroyed = true; } } 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 98579af5835b..7f7d668ea8ac 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -406,8 +406,8 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.BATTERY_VOLTAGE: case FrameworkStatsLog.BATTERY_CYCLE_COUNT: return pullHealthHal(atomTag, data); - case FrameworkStatsLog.APP_FEATURES_OPS: - return pullAppFeaturesOps(atomTag, data); + case FrameworkStatsLog.ATTRIBUTED_APP_OPS: + return pullAttributedAppOps(atomTag, data); default: throw new UnsupportedOperationException("Unknown tagId=" + atomTag); } @@ -562,7 +562,7 @@ public class StatsPullAtomService extends SystemService { registerAppsOnExternalStorageInfo(); registerFaceSettings(); registerAppOps(); - registerAppFeaturesOps(); + registerAttributedAppOps(); registerRuntimeAppOpAccessMessage(); registerNotificationRemoteViews(); registerDangerousPermissionState(); @@ -2898,8 +2898,8 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } - private void registerAppFeaturesOps() { - int tagId = FrameworkStatsLog.APP_FEATURES_OPS; + private void registerAttributedAppOps() { + int tagId = FrameworkStatsLog.ATTRIBUTED_APP_OPS; mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values @@ -2908,7 +2908,7 @@ public class StatsPullAtomService extends SystemService { ); } - int pullAppFeaturesOps(int atomTag, List<StatsEvent> pulledData) { + int pullAttributedAppOps(int atomTag, List<StatsEvent> pulledData) { final long token = Binder.clearCallingIdentity(); try { AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); @@ -2946,7 +2946,7 @@ public class StatsPullAtomService extends SystemService { appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete); HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - return processHistoricalOps(histOps, FrameworkStatsLog.APP_FEATURES_OPS, null); + return processHistoricalOps(histOps, FrameworkStatsLog.ATTRIBUTED_APP_OPS, null); } int processHistoricalOps(HistoricalOps histOps, int atomTag, List<StatsEvent> pulledData) { @@ -2956,15 +2956,15 @@ public class StatsPullAtomService extends SystemService { final int uid = uidOps.getUid(); for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) { final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx); - if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { - for (int featureIdx = 0; featureIdx < packageOps.getFeatureCount(); - featureIdx++) { - final AppOpsManager.HistoricalFeatureOps featureOps = - packageOps.getFeatureOpsAt(featureIdx); - for (int opIdx = 0; opIdx < featureOps.getOpCount(); opIdx++) { - final AppOpsManager.HistoricalOp op = featureOps.getOpAt(opIdx); + if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) { + for (int attributionIdx = 0; + attributionIdx < packageOps.getAttributedOpsCount(); attributionIdx++) { + final AppOpsManager.AttributedHistoricalOps attributedOps = + packageOps.getAttributedOpsAt(attributionIdx); + for (int opIdx = 0; opIdx < attributedOps.getOpCount(); opIdx++) { + final AppOpsManager.HistoricalOp op = attributedOps.getOpAt(opIdx); counter += processHistoricalOp(op, atomTag, pulledData, uid, - packageOps.getPackageName(), featureOps.getFeatureId()); + packageOps.getPackageName(), attributedOps.getTag()); } } } else if (atomTag == FrameworkStatsLog.APP_OPS) { @@ -2981,18 +2981,19 @@ public class StatsPullAtomService extends SystemService { private int processHistoricalOp(AppOpsManager.HistoricalOp op, int atomTag, @Nullable List<StatsEvent> pulledData, int uid, String packageName, - @Nullable String feature) { - if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { + @Nullable String attributionTag) { + if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) { if (pulledData == null) { // this is size estimation call if (op.getForegroundAccessCount(OP_FLAGS_PULLED) + op.getBackgroundAccessCount( OP_FLAGS_PULLED) == 0) { return 0; } else { - return 32 + packageName.length() + (feature == null ? 1 : feature.length()); + return 32 + packageName.length() + (attributionTag == null ? 1 + : attributionTag.length()); } } else { - if (abs((op.getOpCode() + feature + packageName).hashCode() + RANDOM_SEED) % 100 - >= mAppOpsSamplingRate) { + if (abs((op.getOpCode() + attributionTag + packageName).hashCode() + RANDOM_SEED) + % 100 >= mAppOpsSamplingRate) { return 0; } } @@ -3002,10 +3003,10 @@ public class StatsPullAtomService extends SystemService { e.setAtomId(atomTag); e.writeInt(uid); e.writeString(packageName); - if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { - e.writeString(feature); + if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) { + e.writeString(attributionTag); } - if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { + if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) { e.writeString(op.getOpName()); } else { e.writeInt(op.getOpCode()); @@ -3032,7 +3033,7 @@ public class StatsPullAtomService extends SystemService { e.writeBoolean(false); } } - if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { + if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) { e.writeInt(mAppOpsSamplingRate); } pulledData.add(e.build()); @@ -3055,10 +3056,10 @@ public class StatsPullAtomService extends SystemService { e.writeInt(message.getUid()); e.writeString(message.getPackageName()); e.writeString(message.getOp()); - if (message.getFeatureId() == null) { + if (message.getAttributionTag() == null) { e.writeString(""); } else { - e.writeString(message.getFeatureId()); + e.writeString(message.getAttributionTag()); } e.writeString(message.getMessage()); e.writeInt(message.getSamplingStrategy()); diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index 816452679c45..74a6383bc01c 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -41,6 +41,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.textclassifier.ConversationActions; import android.view.textclassifier.SelectionEvent; +import android.view.textclassifier.SystemTextClassifierMetadata; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassificationConstants; import android.view.textclassifier.TextClassificationContext; @@ -179,12 +180,12 @@ public final class TextClassificationManagerService extends ITextClassifierServi TextSelection.Request request, ITextClassifierCallback callback) throws RemoteException { Objects.requireNonNull(request); + Objects.requireNonNull(request.getSystemTextClassifierMetadata()); handleRequest( - request.getUserId(), - request.getCallingPackageName(), + request.getSystemTextClassifierMetadata(), + /* verifyCallingPackage= */ true, /* attemptToBind= */ true, - request.getUseDefaultTextClassifier(), service -> service.onSuggestSelection(sessionId, request, callback), "onSuggestSelection", callback); @@ -196,12 +197,12 @@ public final class TextClassificationManagerService extends ITextClassifierServi TextClassification.Request request, ITextClassifierCallback callback) throws RemoteException { Objects.requireNonNull(request); + Objects.requireNonNull(request.getSystemTextClassifierMetadata()); handleRequest( - request.getUserId(), - request.getCallingPackageName(), + request.getSystemTextClassifierMetadata(), + /* verifyCallingPackage= */ true, /* attemptToBind= */ true, - request.getUseDefaultTextClassifier(), service -> service.onClassifyText(sessionId, request, callback), "onClassifyText", callback); @@ -213,12 +214,12 @@ public final class TextClassificationManagerService extends ITextClassifierServi TextLinks.Request request, ITextClassifierCallback callback) throws RemoteException { Objects.requireNonNull(request); + Objects.requireNonNull(request.getSystemTextClassifierMetadata()); handleRequest( - request.getUserId(), - request.getCallingPackageName(), + request.getSystemTextClassifierMetadata(), + /* verifyCallingPackage= */ true, /* attemptToBind= */ true, - request.getUseDefaultTextClassifier(), service -> service.onGenerateLinks(sessionId, request, callback), "onGenerateLinks", callback); @@ -229,12 +230,12 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Nullable TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException { Objects.requireNonNull(event); + Objects.requireNonNull(event.getSystemTextClassifierMetadata()); handleRequest( - event.getUserId(), - /* callingPackageName= */ null, + event.getSystemTextClassifierMetadata(), + /* verifyCallingPackage= */ false, /* attemptToBind= */ false, - event.getUseDefaultTextClassifier(), service -> service.onSelectionEvent(sessionId, event), "onSelectionEvent", NO_OP_CALLBACK); @@ -246,18 +247,14 @@ public final class TextClassificationManagerService extends ITextClassifierServi TextClassifierEvent event) throws RemoteException { Objects.requireNonNull(event); - final int userId = event.getEventContext() == null - ? UserHandle.getCallingUserId() - : event.getEventContext().getUserId(); - final boolean useDefaultTextClassifier = - event.getEventContext() != null - ? event.getEventContext().getUseDefaultTextClassifier() - : true; + final TextClassificationContext eventContext = event.getEventContext(); + final SystemTextClassifierMetadata systemTcMetadata = + eventContext != null ? eventContext.getSystemTextClassifierMetadata() : null; + handleRequest( - userId, - /* callingPackageName= */ null, + systemTcMetadata, + /* verifyCallingPackage= */ false, /* attemptToBind= */ false, - useDefaultTextClassifier, service -> service.onTextClassifierEvent(sessionId, event), "onTextClassifierEvent", NO_OP_CALLBACK); @@ -269,12 +266,12 @@ public final class TextClassificationManagerService extends ITextClassifierServi TextLanguage.Request request, ITextClassifierCallback callback) throws RemoteException { Objects.requireNonNull(request); + Objects.requireNonNull(request.getSystemTextClassifierMetadata()); handleRequest( - request.getUserId(), - request.getCallingPackageName(), + request.getSystemTextClassifierMetadata(), + /* verifyCallingPackage= */ true, /* attemptToBind= */ true, - request.getUseDefaultTextClassifier(), service -> service.onDetectLanguage(sessionId, request, callback), "onDetectLanguage", callback); @@ -286,12 +283,12 @@ public final class TextClassificationManagerService extends ITextClassifierServi ConversationActions.Request request, ITextClassifierCallback callback) throws RemoteException { Objects.requireNonNull(request); + Objects.requireNonNull(request.getSystemTextClassifierMetadata()); handleRequest( - request.getUserId(), - request.getCallingPackageName(), + request.getSystemTextClassifierMetadata(), + /* verifyCallingPackage= */ true, /* attemptToBind= */ true, - request.getUseDefaultTextClassifier(), service -> service.onSuggestConversationActions(sessionId, request, callback), "onSuggestConversationActions", callback); @@ -303,13 +300,12 @@ public final class TextClassificationManagerService extends ITextClassifierServi throws RemoteException { Objects.requireNonNull(sessionId); Objects.requireNonNull(classificationContext); + Objects.requireNonNull(classificationContext.getSystemTextClassifierMetadata()); - final int userId = classificationContext.getUserId(); handleRequest( - userId, - classificationContext.getPackageName(), + classificationContext.getSystemTextClassifierMetadata(), + /* verifyCallingPackage= */ true, /* attemptToBind= */ false, - classificationContext.getUseDefaultTextClassifier(), service -> { service.onCreateTextClassificationSession(classificationContext, sessionId); mSessionCache.put(sessionId, classificationContext); @@ -333,11 +329,13 @@ public final class TextClassificationManagerService extends ITextClassifierServi textClassificationContext != null ? textClassificationContext.useDefaultTextClassifier : true; + final SystemTextClassifierMetadata sysTcMetadata = new SystemTextClassifierMetadata( + "", userId, useDefaultTextClassifier); + handleRequest( - userId, - /* callingPackageName= */ null, + sysTcMetadata, + /* verifyCallingPackage= */ false, /* attemptToBind= */ false, - useDefaultTextClassifier, service -> { service.onDestroyTextClassificationSession(sessionId); mSessionCache.remove(sessionId); @@ -412,10 +410,9 @@ public final class TextClassificationManagerService extends ITextClassifierServi } private void handleRequest( - @UserIdInt int userId, - @Nullable String callingPackageName, + @Nullable SystemTextClassifierMetadata sysTcMetadata, + boolean verifyCallingPackage, boolean attemptToBind, - boolean useDefaultTextClassifier, @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer, @NonNull String methodName, @NonNull ITextClassifierCallback callback) throws RemoteException { @@ -423,8 +420,17 @@ public final class TextClassificationManagerService extends ITextClassifierServi Objects.requireNonNull(methodName); Objects.requireNonNull(callback); + final int userId = + sysTcMetadata == null ? UserHandle.getCallingUserId() : sysTcMetadata.getUserId(); + final String callingPackageName = + sysTcMetadata == null ? null : sysTcMetadata.getCallingPackageName(); + final boolean useDefaultTextClassifier = + sysTcMetadata == null ? true : sysTcMetadata.useDefaultTextClassifier(); + try { - validateCallingPackage(callingPackageName); + if (verifyCallingPackage) { + validateCallingPackage(callingPackageName); + } validateUser(userId); } catch (Exception e) { throw new RemoteException("Invalid request: " + e.getMessage(), e, @@ -636,8 +642,10 @@ public final class TextClassificationManagerService extends ITextClassifierServi public final boolean useDefaultTextClassifier; StrippedTextClassificationContext(TextClassificationContext textClassificationContext) { - userId = textClassificationContext.getUserId(); - useDefaultTextClassifier = textClassificationContext.getUseDefaultTextClassifier(); + SystemTextClassifierMetadata sysTcMetadata = + textClassificationContext.getSystemTextClassifierMetadata(); + userId = sysTcMetadata.getUserId(); + useDefaultTextClassifier = sysTcMetadata.useDefaultTextClassifier(); } } diff --git a/services/core/java/com/android/server/tv/UinputBridge.java b/services/core/java/com/android/server/tv/UinputBridge.java index 752aa6683a88..a2fe5fcde8c2 100644 --- a/services/core/java/com/android/server/tv/UinputBridge.java +++ b/services/core/java/com/android/server/tv/UinputBridge.java @@ -28,7 +28,7 @@ import java.io.IOException; public final class UinputBridge { private final CloseGuard mCloseGuard = CloseGuard.get(); private long mPtr; - private IBinder mToken = null; + private IBinder mToken; private static native long nativeOpen(String name, String uniqueId, int width, int height, int maxPointers); @@ -39,6 +39,25 @@ public final class UinputBridge { private static native void nativeSendPointerUp(long ptr, int pointerId); private static native void nativeSendPointerSync(long ptr); + /** Opens a gamepad - will support gamepad key and axis sending */ + private static native long nativeGamepadOpen(String name, String uniqueId); + + /** Marks the specified key up/down for a gamepad */ + private static native void nativeSendGamepadKey(long ptr, int keyIndex, boolean down); + + /** + * Gamepads pre-define the following axes: + * - Left joystick X, axis == ABS_X == 0, range [0, 254] + * - Left joystick Y, axis == ABS_Y == 1, range [0, 254] + * - Right joystick X, axis == ABS_RX == 3, range [0, 254] + * - Right joystick Y, axis == ABS_RY == 4, range [0, 254] + * - Left trigger, axis == ABS_Z == 2, range [0, 254] + * - Right trigger, axis == ABS_RZ == 5, range [0, 254] + * - DPad X, axis == ABS_HAT0X == 0x10, range [-1, 1] + * - DPad Y, axis == ABS_HAT0Y == 0x11, range [-1, 1] + */ + private static native void nativeSendGamepadAxisValue(long ptr, int axis, int value); + public UinputBridge(IBinder token, String name, int width, int height, int maxPointers) throws IOException { if (width < 1 || height < 1) { @@ -58,12 +77,31 @@ public final class UinputBridge { mCloseGuard.open("close"); } + /** Constructor used by static factory methods */ + private UinputBridge(IBinder token, long ptr) { + mPtr = ptr; + mToken = token; + mCloseGuard.open("close"); + } + + /** Opens a UinputBridge that supports gamepad buttons and axes. */ + public static UinputBridge openGamepad(IBinder token, String name) + throws IOException { + if (token == null) { + throw new IllegalArgumentException("Token cannot be null"); + } + long ptr = nativeGamepadOpen(name, token.toString()); + if (ptr == 0) { + throw new IOException("Could not open uinput device " + name); + } + + return new UinputBridge(token, ptr); + } + @Override protected void finalize() throws Throwable { try { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } + mCloseGuard.warnIfOpen(); close(mToken); } finally { mToken = null; @@ -119,7 +157,35 @@ public final class UinputBridge { if (isTokenValid(token)) { nativeSendPointerSync(mPtr); } + } + + /** Send a gamepad key + * @param keyIndex - the index of the w3-spec key + * @param down - is the key pressed ? + */ + public void sendGamepadKey(IBinder token, int keyIndex, boolean down) { + if (isTokenValid(token)) { + nativeSendGamepadKey(mPtr, keyIndex, down); + } + } + /** Send a gamepad axis value. + * - Left joystick X, axis == ABS_X == 0, range [0, 254] + * - Left joystick Y, axis == ABS_Y == 1, range [0, 254] + * - Right joystick X, axis == ABS_RX == 3, range [0, 254] + * - Right joystick Y, axis == ABS_RY == 4, range [0, 254] + * - Left trigger, axis == ABS_Z == 2, range [0, 254] + * - Right trigger, axis == ABS_RZ == 5, range [0, 254] + * - DPad X, axis == ABS_HAT0X == 0x10, range [-1, 1] + * - DPad Y, axis == ABS_HAT0Y == 0x11, range [-1, 1] + * + * @param axis is the axis index + * @param value is the value to set for that axis + */ + public void sendGamepadAxisValue(IBinder token, int axis, int value) { + if (isTokenValid(token)) { + nativeSendGamepadAxisValue(mPtr, axis, value); + } } public void clear(IBinder token) { diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 04d551d0f84e..b43d8b7daf4a 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -24,6 +24,8 @@ import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ITunerResourceManager; import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerDemuxRequest; +import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; @@ -189,6 +191,24 @@ public class TunerResourceManagerService extends SystemService { } @Override + public boolean requestDemux(@NonNull TunerDemuxRequest request, + @NonNull int[] demuxHandle) { + if (DEBUG) { + Slog.d(TAG, "requestDemux(request=" + request + ")"); + } + return true; + } + + @Override + public boolean requestDescrambler(@NonNull TunerDescramblerRequest request, + @NonNull int[] descrambleHandle) { + if (DEBUG) { + Slog.d(TAG, "requestDescrambler(request=" + request + ")"); + } + return true; + } + + @Override public boolean requestCasSession( @NonNull CasSessionRequest request, @NonNull int[] sessionResourceId) { if (DEBUG) { @@ -214,6 +234,20 @@ public class TunerResourceManagerService extends SystemService { } @Override + public void releaseDemux(int demuxHandle) { + if (DEBUG) { + Slog.d(TAG, "releaseDemux(demuxHandle=" + demuxHandle + ")"); + } + } + + @Override + public void releaseDescrambler(int descramblerHandle) { + if (DEBUG) { + Slog.d(TAG, "releaseDescrambler(descramblerHandle=" + descramblerHandle + ")"); + } + } + + @Override public void releaseCasSession(int sessionResourceId) { if (DEBUG) { Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")"); diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java index 761fbf8a0622..88a60ddc5a1e 100644 --- a/services/core/java/com/android/server/twilight/TwilightService.java +++ b/services/core/java/com/android/server/twilight/TwilightService.java @@ -50,7 +50,7 @@ public final class TwilightService extends SystemService implements AlarmManager.OnAlarmListener, Handler.Callback, LocationListener { private static final String TAG = "TwilightService"; - private static final String FEATURE_ID = "TwilightService"; + private static final String ATTRIBUTION_TAG = "TwilightService"; private static final boolean DEBUG = false; private static final int MSG_START_LISTENING = 1; @@ -74,7 +74,7 @@ public final class TwilightService extends SystemService protected TwilightState mLastTwilightState; public TwilightService(Context context) { - super(context.createFeatureContext(FEATURE_ID)); + super(context.createAttributionContext(ATTRIBUTION_TAG)); mHandler = new Handler(Looper.getMainLooper(), this); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 6eb3c0fb06fb..a298b8915110 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2184,47 +2184,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - /** - * Called when the wallpaper needs to zoom out. - * - * @param zoom from 0 to 1 (inclusive) where 1 means fully zoomed out, 0 means fully zoomed in. - * @param callingPackage package name calling this API. - * @param displayId id of the display whose zoom is updating. - */ - public void setWallpaperZoomOut(float zoom, String callingPackage, int displayId) { - if (!isWallpaperSupported(callingPackage)) { - return; - } - synchronized (mLock) { - if (!isValidDisplay(displayId)) { - throw new IllegalArgumentException("Cannot find display with id=" + displayId); - } - int userId = UserHandle.getCallingUserId(); - if (mCurrentUserId != userId) { - return; // Don't change the properties now - } - WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); - if (zoom < 0 || zoom > 1f) { - throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom); - } - - if (wallpaper.connection != null) { - final WallpaperConnection.DisplayConnector connector = wallpaper.connection - .getDisplayConnectorOrCreate(displayId); - final IWallpaperEngine engine = connector != null ? connector.mEngine : null; - if (engine != null) { - try { - engine.setZoomOut(zoom); - } catch (RemoteException e) { - if (DEBUG) { - Slog.w(TAG, "Couldn't set wallpaper zoom", e); - } - } - } - } - } - } - @Deprecated @Override public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb, diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b6ad24184803..e73c928ef925 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1589,6 +1589,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A info.taskAffinity = uid + ":" + info.taskAffinity; } taskAffinity = info.taskAffinity; + if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null + && !info.windowLayout.windowLayoutAffinity.startsWith(uid)) { + info.windowLayout.windowLayoutAffinity = + uid + ":" + info.windowLayout.windowLayoutAffinity; + } stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0; nonLocalizedLabel = aInfo.nonLocalizedLabel; labelRes = aInfo.labelRes; diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 9bad799b68d1..4ebb4236b84f 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -153,7 +153,6 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.DisplayInfo; -import android.view.ITaskOrganizer; import android.view.SurfaceControl; import com.android.internal.annotations.GuardedBy; @@ -307,8 +306,6 @@ class ActivityStack extends Task { // TODO(task-hierarchy): remove when tiles can be actual parents TaskTile mTile = null; - private int mLastTaskOrganizerWindowingMode = -1; - private final Handler mHandler; private class ActivityStackHandler extends Handler { @@ -635,8 +632,6 @@ class ActivityStack extends Task { super.onConfigurationChanged(newParentConfig); - updateTaskOrganizerState(); - // Only need to update surface size here since the super method will handle updating // surface position. updateSurfaceSize(getPendingTransaction()); @@ -692,30 +687,6 @@ class ActivityStack extends Task { } } - void updateTaskOrganizerState() { - if (!isRootTask()) { - return; - } - - final int windowingMode = getWindowingMode(); - if (windowingMode == mLastTaskOrganizerWindowingMode) { - // If our windowing mode hasn't actually changed, then just stick - // with our old organizer. This lets us implement the semantic - // where SysUI can continue to manage it's old tasks - // while CTS temporarily takes over the registration. - return; - } - /* - * Different windowing modes may be managed by different task organizers. If - * getTaskOrganizer returns null, we still call setTaskOrganizer to - * make sure we clear it. - */ - final ITaskOrganizer org = - mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode); - setTaskOrganizer(org); - mLastTaskOrganizerWindowingMode = windowingMode; - } - @Override public void setWindowingMode(int windowingMode) { // Calling Task#setWindowingMode() for leaf task since this is the a specialization of diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 688c9ae705d7..c7a139176f6c 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -29,7 +29,6 @@ import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.WaitResult.LAUNCH_STATE_COLD; import static android.app.WaitResult.LAUNCH_STATE_HOT; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -178,6 +177,9 @@ class ActivityStarter { private Intent mNewTaskIntent; private ActivityStack mSourceStack; private ActivityStack mTargetStack; + // The task that the last activity was started into. We currently reset the actual start + // activity's task and as a result may not have a reference to the task in all cases + private Task mTargetTask; private boolean mMovedToFront; private boolean mNoAnimation; private boolean mKeepCurTransition; @@ -545,6 +547,7 @@ class ActivityStarter { mNewTaskIntent = starter.mNewTaskIntent; mSourceStack = starter.mSourceStack; + mTargetTask = starter.mTargetTask; mTargetStack = starter.mTargetStack; mMovedToFront = starter.mMovedToFront; mNoAnimation = starter.mNoAnimation; @@ -1368,7 +1371,10 @@ class ActivityStarter { // it waits for the new activity to become visible instead, {@link #waitResultIfNeeded}. mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result); - if (startedActivityStack == null) { + final Task targetTask = r.getTask() != null + ? r.getTask() + : mTargetTask; + if (startedActivityStack == null || targetTask == null) { return; } @@ -1379,19 +1385,10 @@ class ActivityStarter { // The activity was already running so it wasn't started, but either brought to the // front or the new intent was delivered to it since it was already in front. Notify // anyone interested in this piece of information. - switch (startedActivityStack.getWindowingMode()) { - case WINDOWING_MODE_PINNED: - mService.getTaskChangeNotificationController().notifyPinnedActivityRestartAttempt( - clearedTask); - break; - case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: - final ActivityStack homeStack = - startedActivityStack.getDisplay().getOrCreateRootHomeTask(); - if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) { - mService.mWindowManager.showRecentApps(); - } - break; - } + final ActivityStack homeStack = targetTask.getDisplayContent().getRootHomeTask(); + final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null); + mService.getTaskChangeNotificationController().notifyActivityRestartAttempt( + targetTask.getTaskInfo(), homeTaskVisible, clearedTask); } } @@ -1517,6 +1514,7 @@ class ActivityStarter { // Compute if there is an existing task that should be used for. final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask(); final boolean newTask = targetTask == null; + mTargetTask = targetTask; computeLaunchParams(r, sourceRecord, targetTask); @@ -2018,6 +2016,7 @@ class ActivityStarter { mSourceStack = null; mTargetStack = null; + mTargetTask = null; mMovedToFront = false; mNoAnimation = false; mKeepCurTransition = false; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 21d300aba1b9..7bacc427feb8 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -356,6 +356,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ActivityManagerInternal mAmInternal; UriGrantsManagerInternal mUgmInternal; private PackageManagerInternal mPmInternal; + /** The cached sys ui service component name from package manager. */ + private ComponentName mSysUiServiceComponent; private PermissionPolicyInternal mPermissionPolicyInternal; @VisibleForTesting final ActivityTaskManagerInternal mInternal; @@ -5869,6 +5871,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mPmInternal; } + ComponentName getSysUiServiceComponentLocked() { + if (mSysUiServiceComponent == null) { + final PackageManagerInternal pm = getPackageManagerInternalLocked(); + mSysUiServiceComponent = pm.getSystemUiServiceComponent(); + } + return mSysUiServiceComponent; + } + PermissionPolicyInternal getPermissionPolicyInternal() { if (mPermissionPolicyInternal == null) { mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 3352bd5b0096..98e3d07fc2e4 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2224,9 +2224,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo .addTaggedData(MetricsEvent.FIELD_DISPLAY_ID, getDisplayId())); } - // If there was no pinned stack, we still need to notify the controller of the display info - // update as a result of the config change. - if (mPinnedStackControllerLocked != null && !hasPinnedTask()) { + if (mPinnedStackControllerLocked != null) { mPinnedStackControllerLocked.onDisplayInfoChanged(getDisplayInfo()); } } @@ -4934,6 +4932,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo scheduleAnimation(); } } + + @Override + boolean shouldMagnify() { + // Omitted from Screen-Magnification + return false; + } } /** diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index d5a0d05b0f84..ba61667cc871 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -28,6 +28,7 @@ import static android.view.Display.TYPE_INTERNAL; import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; +import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -439,10 +440,12 @@ public class DisplayPolicy { updateDreamingSleepToken(msg.arg1 != 0); break; case MSG_REQUEST_TRANSIENT_BARS: - WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS) - ? mStatusBar : mNavigationBar; - if (targetBar != null) { - requestTransientBars(targetBar); + synchronized (mLock) { + WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS) + ? mStatusBar : mNavigationBar; + if (targetBar != null) { + requestTransientBars(targetBar); + } } break; case MSG_DISPOSE_INPUT_CONSUMER: @@ -498,15 +501,20 @@ public class DisplayPolicy { new SystemGesturesPointerEventListener.Callbacks() { @Override public void onSwipeFromTop() { - if (mStatusBar != null) { - requestTransientBars(mStatusBar); + synchronized (mLock) { + if (mStatusBar != null) { + requestTransientBars(mStatusBar); + } } } @Override public void onSwipeFromBottom() { - if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) { - requestTransientBars(mNavigationBar); + synchronized (mLock) { + if (mNavigationBar != null + && mNavigationBarPosition == NAV_BAR_BOTTOM) { + requestTransientBars(mNavigationBar); + } } } @@ -516,12 +524,13 @@ public class DisplayPolicy { synchronized (mLock) { mDisplayContent.calculateSystemGestureExclusion( excludedRegion, null /* outUnrestricted */); - } - final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture - || mNavigationBarPosition == NAV_BAR_RIGHT; - if (mNavigationBar != null && sideAllowed - && !mSystemGestures.currentGestureStartedInRegion(excludedRegion)) { - requestTransientBars(mNavigationBar); + final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture + || mNavigationBarPosition == NAV_BAR_RIGHT; + if (mNavigationBar != null && sideAllowed + && !mSystemGestures.currentGestureStartedInRegion( + excludedRegion)) { + requestTransientBars(mNavigationBar); + } } excludedRegion.recycle(); } @@ -532,12 +541,13 @@ public class DisplayPolicy { synchronized (mLock) { mDisplayContent.calculateSystemGestureExclusion( excludedRegion, null /* outUnrestricted */); - } - final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture - || mNavigationBarPosition == NAV_BAR_LEFT; - if (mNavigationBar != null && sideAllowed - && !mSystemGestures.currentGestureStartedInRegion(excludedRegion)) { - requestTransientBars(mNavigationBar); + final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture + || mNavigationBarPosition == NAV_BAR_LEFT; + if (mNavigationBar != null && sideAllowed + && !mSystemGestures.currentGestureStartedInRegion( + excludedRegion)) { + requestTransientBars(mNavigationBar); + } } excludedRegion.recycle(); } @@ -1469,8 +1479,14 @@ public class DisplayPolicy { */ public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { displayFrames.onBeginLayout(); - updateInsetsStateForDisplayCutout(displayFrames, - mDisplayContent.getInsetsStateController().getRawInsetsState()); + final InsetsState insetsState = + mDisplayContent.getInsetsStateController().getRawInsetsState(); + + // Reset the frame of IME so that the layout of windows above IME won't get influenced. + // Once we layout the IME, frames will be set again on the source. + insetsState.getSource(ITYPE_IME).setFrame(0, 0, 0, 0); + + updateInsetsStateForDisplayCutout(displayFrames, insetsState); mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); @@ -3149,47 +3165,46 @@ public class DisplayPolicy { } private void requestTransientBars(WindowState swipeTarget) { - synchronized (mLock) { - if (!mService.mPolicy.isUserSetupComplete()) { - // Swipe-up for navigation bar is disabled during setup + if (!mService.mPolicy.isUserSetupComplete()) { + // Swipe-up for navigation bar is disabled during setup + return; + } + if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) { + if (swipeTarget == mNavigationBar + && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) { + // Don't show status bar when swiping on already visible navigation bar return; } - if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) { - if (swipeTarget == mNavigationBar - && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) { - // Don't show status bar when swiping on already visible navigation bar - return; - } - final InsetsControlTarget controlTarget = - swipeTarget.getControllableInsetProvider().getControlTarget(); + final InsetsSourceProvider provider = swipeTarget.getControllableInsetProvider(); + final InsetsControlTarget controlTarget = provider != null + ? provider.getControlTarget() : null; - // No transient mode on lockscreen (in notification shade window). - if (controlTarget == null || controlTarget == getNotificationShade()) { - return; - } - if (controlTarget.canShowTransient()) { - mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap( - new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); - } else { - controlTarget.showInsets(Type.systemBars(), false); - } + // No transient mode on lockscreen (in notification shade window). + if (controlTarget == null || controlTarget == getNotificationShade()) { + return; + } + if (controlTarget.canShowTransient()) { + mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap( + new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); } else { - boolean sb = mStatusBarController.checkShowTransientBarLw(); - boolean nb = mNavigationBarController.checkShowTransientBarLw() - && !isNavBarEmpty(mLastSystemUiFlags); - if (sb || nb) { - // Don't show status bar when swiping on already visible navigation bar - if (!nb && swipeTarget == mNavigationBar) { - if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target"); - return; - } - if (sb) mStatusBarController.showTransient(); - if (nb) mNavigationBarController.showTransient(); - updateSystemUiVisibilityLw(); + controlTarget.showInsets(Type.systemBars(), false); + } + } else { + boolean sb = mStatusBarController.checkShowTransientBarLw(); + boolean nb = mNavigationBarController.checkShowTransientBarLw() + && !isNavBarEmpty(mLastSystemUiFlags); + if (sb || nb) { + // Don't show status bar when swiping on already visible navigation bar + if (!nb && swipeTarget == mNavigationBar) { + if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target"); + return; } + if (sb) mStatusBarController.showTransient(); + if (nb) mNavigationBarController.showTransient(); + updateSystemUiVisibilityLw(); } - mImmersiveModeConfirmation.confirmCurrentPrompt(); } + mImmersiveModeConfirmation.confirmCurrentPrompt(); } private void disposeInputConsumer(InputConsumer inputConsumer) { diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 01f98888c777..30912e55f908 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -36,6 +36,7 @@ import android.util.IntArray; import android.util.SparseArray; import android.view.InsetsAnimationControlCallbacks; import android.view.InsetsAnimationControlImpl; +import android.view.InsetsAnimationControlRunner; import android.view.InsetsController; import android.view.InsetsSourceControl; import android.view.InsetsState; @@ -44,6 +45,7 @@ import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; import android.view.ViewRootImpl; import android.view.WindowInsetsAnimation; +import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowInsetsAnimationControlListener; import com.android.internal.annotations.VisibleForTesting; @@ -327,7 +329,7 @@ class InsetsPolicy { InsetsPolicyAnimationControlCallbacks mControlCallbacks; InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback) { - super(show); + super(show, true /* useSfVsync */); mFinishCallback = finishCallback; mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this); } @@ -360,8 +362,6 @@ class InsetsPolicy { mFocusedWin.getDisplayContent().getBounds(), getState(), mListener, typesReady, this, mListener.getDurationMs(), InsetsController.INTERPOLATOR, true, - show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN - : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE); SurfaceAnimationThread.getHandler().post( () -> mListener.onReady(mAnimationControl, typesReady)); @@ -377,7 +377,7 @@ class InsetsPolicy { } @Override - public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) { + public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { // Nothing's needed here. Finish steps is handled in the listener // onAnimationFinished callback. } @@ -406,14 +406,21 @@ class InsetsPolicy { applyParams(t, surfaceParams, mTmpFloat9); } t.apply(); + t.close(); + } + + // Since we don't push applySurfaceParams to a Handler-queue we don't need + // to push release in this case. + @Override + public void releaseSurfaceControlFromRt(SurfaceControl sc) { + sc.release(); } @Override public void startAnimation(InsetsAnimationControlImpl controller, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, - WindowInsetsAnimation.Bounds bounds, - int layoutDuringAnimation) { + Bounds bounds) { } } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 6ff029bfaea1..ee36db9d94ee 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_INVALID; @@ -29,6 +30,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.WindowConfiguration; +import android.app.WindowConfiguration.WindowingMode; import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; @@ -74,30 +77,28 @@ class InsetsStateController { /** * When dispatching window state to the client, we'll need to exclude the source that represents - * the window that is being dispatched. + * the window that is being dispatched. We also need to exclude certain types of insets source + * for client within specific windowing modes. * * @param target The client we dispatch the state to. * @return The state stripped of the necessary information. */ InsetsState getInsetsForDispatch(@NonNull WindowState target) { final InsetsSourceProvider provider = target.getControllableInsetProvider(); - if (provider == null) { - return mState; - } - final @InternalInsetsType int type = provider.getSource().getType(); - return getInsetsForType(type); + final @InternalInsetsType int type = provider != null + ? provider.getSource().getType() : ITYPE_INVALID; + return getInsetsForTypeAndWindowingMode(type, target.getWindowingMode()); } InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { final @InternalInsetsType int type = getInsetsTypeForWindowType(attrs.type); - if (type == ITYPE_INVALID) { - return mState; - } - return getInsetsForType(type); + final WindowToken token = mDisplayContent.getWindowToken(attrs.token); + final @WindowingMode int windowingMode = token != null + ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; + return getInsetsForTypeAndWindowingMode(type, windowingMode); } - @InternalInsetsType - private static int getInsetsTypeForWindowType(int type) { + private static @InternalInsetsType int getInsetsTypeForWindowType(int type) { switch(type) { case TYPE_STATUS_BAR: return ITYPE_STATUS_BAR; @@ -110,36 +111,48 @@ class InsetsStateController { } } - private InsetsState getInsetsForType(@InternalInsetsType int type) { - final InsetsState state = new InsetsState(); - state.set(mState); - state.removeSource(type); + /** @see #getInsetsForDispatch */ + private InsetsState getInsetsForTypeAndWindowingMode(@InternalInsetsType int type, + @WindowingMode int windowingMode) { + InsetsState state = mState; - // Navigation bar doesn't get influenced by anything else - if (type == ITYPE_NAVIGATION_BAR) { - state.removeSource(ITYPE_IME); - state.removeSource(ITYPE_STATUS_BAR); - state.removeSource(ITYPE_CAPTION_BAR); - } + if (type != ITYPE_INVALID) { + state = new InsetsState(state); + state.removeSource(type); - // Status bar doesn't get influenced by caption bar - if (type == ITYPE_STATUS_BAR) { - state.removeSource(ITYPE_CAPTION_BAR); - } + // Navigation bar doesn't get influenced by anything else + if (type == ITYPE_NAVIGATION_BAR) { + state.removeSource(ITYPE_IME); + state.removeSource(ITYPE_STATUS_BAR); + state.removeSource(ITYPE_CAPTION_BAR); + } - // IME needs different frames for certain cases (e.g. navigation bar in gesture nav). - if (type == ITYPE_IME) { - for (int i = mProviders.size() - 1; i >= 0; i--) { - InsetsSourceProvider otherProvider = mProviders.valueAt(i); - if (otherProvider.overridesImeFrame()) { - InsetsSource override = - new InsetsSource(state.getSource(otherProvider.getSource().getType())); - override.setFrame(otherProvider.getImeOverrideFrame()); - state.addSource(override); + // Status bar doesn't get influenced by caption bar + if (type == ITYPE_STATUS_BAR) { + state.removeSource(ITYPE_CAPTION_BAR); + } + + // IME needs different frames for certain cases (e.g. navigation bar in gesture nav). + if (type == ITYPE_IME) { + for (int i = mProviders.size() - 1; i >= 0; i--) { + InsetsSourceProvider otherProvider = mProviders.valueAt(i); + if (otherProvider.overridesImeFrame()) { + InsetsSource override = + new InsetsSource( + state.getSource(otherProvider.getSource().getType())); + override.setFrame(otherProvider.getImeOverrideFrame()); + state.addSource(override); + } } } } + if (WindowConfiguration.isFloating(windowingMode)) { + state = new InsetsState(state); + state.removeSource(ITYPE_STATUS_BAR); + state.removeSource(ITYPE_NAVIGATION_BAR); + } + return state; } diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index 660706e4139b..9371c0eec26f 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -16,7 +16,9 @@ package com.android.server.wm; +import android.annotation.Nullable; import android.content.ComponentName; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManagerInternal; import android.graphics.Rect; import android.os.Environment; @@ -87,9 +89,17 @@ class LaunchParamsPersister { * launching activity of tasks) to {@link PersistableLaunchParams} that stores launch metadata * that are stable across reboots. */ - private final SparseArray<ArrayMap<ComponentName, PersistableLaunchParams>> mMap = + private final SparseArray<ArrayMap<ComponentName, PersistableLaunchParams>> mLaunchParamsMap = new SparseArray<>(); + /** + * A map from {@link android.content.pm.ActivityInfo.WindowLayout#windowLayoutAffinity} to + * activity's component name for reverse queries from window layout affinities to activities. + * Used to decide if we should use another activity's record with the same affinity. + */ + private final ArrayMap<String, ArraySet<ComponentName>> mWindowLayoutAffinityMap = + new ArrayMap<>(); + LaunchParamsPersister(PersisterQueue persisterQueue, ActivityStackSupervisor supervisor) { this(persisterQueue, supervisor, Environment::getDataSystemCeDirectory); } @@ -112,7 +122,7 @@ class LaunchParamsPersister { } void onCleanupUser(int userId) { - mMap.remove(userId); + mLaunchParamsMap.remove(userId); } private void loadLaunchParams(int userId) { @@ -128,7 +138,7 @@ class LaunchParamsPersister { final File[] paramsFiles = launchParamsFolder.listFiles(); final ArrayMap<ComponentName, PersistableLaunchParams> map = new ArrayMap<>(paramsFiles.length); - mMap.put(userId, map); + mLaunchParamsMap.put(userId, map); for (File paramsFile : paramsFiles) { if (!paramsFile.isFile()) { @@ -179,10 +189,12 @@ class LaunchParamsPersister { continue; } - params.restoreFromXml(parser); + params.restore(paramsFile, parser); } map.put(name, params); + addComponentNameToLaunchParamAffinityMapIfNotNull( + name, params.mWindowLayoutAffinity); } catch (Exception e) { Slog.w(TAG, "Failed to restore launch params for " + name, e); filesToDelete.add(paramsFile); @@ -204,19 +216,17 @@ class LaunchParamsPersister { final ComponentName name = task.realActivity; final int userId = task.mUserId; PersistableLaunchParams params; - ArrayMap<ComponentName, PersistableLaunchParams> map = mMap.get(userId); + ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId); if (map == null) { map = new ArrayMap<>(); - mMap.put(userId, map); + mLaunchParamsMap.put(userId, map); } - params = map.get(name); - if (params == null) { - params = new PersistableLaunchParams(); - map.put(name, params); - } + params = map.computeIfAbsent(name, componentName -> new PersistableLaunchParams()); final boolean changed = saveTaskToLaunchParam(task, display, params); + addComponentNameToLaunchParamAffinityMapIfNotNull(name, params.mWindowLayoutAffinity); + if (changed) { mPersisterQueue.updateLastOrAddItem( new LaunchParamsWriteQueueItem(userId, name, params), @@ -243,19 +253,63 @@ class LaunchParamsPersister { params.mBounds.setEmpty(); } + String launchParamAffinity = task.mWindowLayoutAffinity; + changed |= Objects.equals(launchParamAffinity, params.mWindowLayoutAffinity); + params.mWindowLayoutAffinity = launchParamAffinity; + + if (changed) { + params.mTimestamp = System.currentTimeMillis(); + } + return changed; } + private void addComponentNameToLaunchParamAffinityMapIfNotNull( + ComponentName name, String launchParamAffinity) { + if (launchParamAffinity == null) { + return; + } + mWindowLayoutAffinityMap.computeIfAbsent(launchParamAffinity, affinity -> new ArraySet<>()) + .add(name); + } + void getLaunchParams(Task task, ActivityRecord activity, LaunchParams outParams) { final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent; final int userId = task != null ? task.mUserId : activity.mUserId; + final String windowLayoutAffinity; + if (task != null) { + windowLayoutAffinity = task.mWindowLayoutAffinity; + } else { + ActivityInfo.WindowLayout layout = activity.info.windowLayout; + windowLayoutAffinity = layout == null ? null : layout.windowLayoutAffinity; + } outParams.reset(); - Map<ComponentName, PersistableLaunchParams> map = mMap.get(userId); + Map<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId); if (map == null) { return; } - final PersistableLaunchParams persistableParams = map.get(name); + + // First use its own record as a reference. + PersistableLaunchParams persistableParams = map.get(name); + // Next we'll compare these params against all existing params with the same affinity and + // use the newest one. + if (windowLayoutAffinity != null + && mWindowLayoutAffinityMap.get(windowLayoutAffinity) != null) { + ArraySet<ComponentName> candidates = mWindowLayoutAffinityMap.get(windowLayoutAffinity); + for (int i = 0; i < candidates.size(); ++i) { + ComponentName candidate = candidates.valueAt(i); + final PersistableLaunchParams candidateParams = map.get(candidate); + if (candidateParams == null) { + continue; + } + + if (persistableParams == null + || candidateParams.mTimestamp > persistableParams.mTimestamp) { + persistableParams = candidateParams; + } + } + } if (persistableParams == null) { return; @@ -272,10 +326,10 @@ class LaunchParamsPersister { void removeRecordForPackage(String packageName) { final List<File> fileToDelete = new ArrayList<>(); - for (int i = 0; i < mMap.size(); ++i) { - int userId = mMap.keyAt(i); + for (int i = 0; i < mLaunchParamsMap.size(); ++i) { + int userId = mLaunchParamsMap.keyAt(i); final File launchParamsFolder = getLaunchParamFolder(userId); - ArrayMap<ComponentName, PersistableLaunchParams> map = mMap.valueAt(i); + ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.valueAt(i); for (int j = map.size() - 1; j >= 0; --j) { final ComponentName name = map.keyAt(j); if (name.getPackageName().equals(packageName)) { @@ -409,6 +463,7 @@ class LaunchParamsPersister { private static final String ATTR_WINDOWING_MODE = "windowing_mode"; private static final String ATTR_DISPLAY_UNIQUE_ID = "display_unique_id"; private static final String ATTR_BOUNDS = "bounds"; + private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity"; /** The bounds within the parent container. */ final Rect mBounds = new Rect(); @@ -419,14 +474,29 @@ class LaunchParamsPersister { /** The windowing mode to be in. */ int mWindowingMode; + /** + * Last {@link android.content.pm.ActivityInfo.WindowLayout#windowLayoutAffinity} of the + * window. + */ + @Nullable String mWindowLayoutAffinity; + + /** + * Timestamp from {@link System#currentTimeMillis()} when this record is captured, or last + * modified time when the record is restored from storage. + */ + long mTimestamp; + void saveToXml(XmlSerializer serializer) throws IOException { serializer.attribute(null, ATTR_DISPLAY_UNIQUE_ID, mDisplayUniqueId); serializer.attribute(null, ATTR_WINDOWING_MODE, Integer.toString(mWindowingMode)); serializer.attribute(null, ATTR_BOUNDS, mBounds.flattenToString()); + if (mWindowLayoutAffinity != null) { + serializer.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity); + } } - void restoreFromXml(XmlPullParser parser) { + void restore(File xmlFile, XmlPullParser parser) { for (int i = 0; i < parser.getAttributeCount(); ++i) { final String attrValue = parser.getAttributeValue(i); switch (parser.getAttributeName(i)) { @@ -443,16 +513,28 @@ class LaunchParamsPersister { } break; } + case ATTR_WINDOW_LAYOUT_AFFINITY: + mWindowLayoutAffinity = attrValue; + break; } } + + // The modified time could be a few seconds later than the timestamp when the record is + // captured, which is a good enough estimate to the capture time after a reboot or a + // user switch. + mTimestamp = xmlFile.lastModified(); } @Override public String toString() { final StringBuilder builder = new StringBuilder("PersistableLaunchParams{"); - builder.append("windowingMode=" + mWindowingMode); + builder.append(" windowingMode=" + mWindowingMode); builder.append(" displayUniqueId=" + mDisplayUniqueId); builder.append(" bounds=" + mBounds); + if (mWindowLayoutAffinity != null) { + builder.append(" launchParamsAffinity=" + mWindowLayoutAffinity); + } + builder.append(" timestamp=" + mTimestamp); builder.append(" }"); return builder.toString(); } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 9df42486580b..5f3732a58824 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -356,6 +356,31 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override + public void setWallpaperZoomOut(IBinder window, float zoom) { + if (Float.compare(0f, zoom) > 0 || Float.compare(1f, zoom) < 0 || Float.isNaN(zoom)) { + throw new IllegalArgumentException("Zoom must be a valid float between 0 and 1: " + + zoom); + } + synchronized (mService.mGlobalLock) { + long ident = Binder.clearCallingIdentity(); + try { + actionOnWallpaper(window, (wpController, windowState) -> + wpController.setWallpaperZoomOut(windowState, zoom)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + @Override + public void setShouldZoomOutWallpaper(IBinder window, boolean shouldZoom) { + synchronized (mService.mGlobalLock) { + actionOnWallpaper(window, (wpController, windowState) -> + wpController.setShouldZoomOutWallpaper(windowState, shouldZoom)); + } + } + + @Override public void wallpaperOffsetsComplete(IBinder window) { synchronized (mService.mGlobalLock) { actionOnWallpaper(window, (wpController, windowState) -> diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index b2db99b06cde..5ab5fbcf693d 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -199,6 +199,7 @@ class Task extends WindowContainer<WindowContainer> { private static final String ATTR_MIN_WIDTH = "min_width"; private static final String ATTR_MIN_HEIGHT = "min_height"; private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version"; + private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity"; // Current version of the task record we persist. Used to check if we need to run any upgrade // code. @@ -233,6 +234,8 @@ class Task extends WindowContainer<WindowContainer> { String affinity; // The affinity name for this task, or null; may change identity. String rootAffinity; // Initial base affinity, or null; does not change from initial root. + String mWindowLayoutAffinity; // Launch param affinity of this task or null. Used when saving + // launch params of this task. IVoiceInteractionSession voiceSession; // Voice interaction session driving task IVoiceInteractor voiceInteractor; // Associated interactor to provide to app Intent intent; // The original intent that started the task. Note that this value can @@ -474,6 +477,7 @@ class Task extends WindowContainer<WindowContainer> { * taskAppeared callback, and emit a taskRemoved callback when the Task is vanished. */ ITaskOrganizer mTaskOrganizer; + private int mLastTaskOrganizerWindowingMode = -1; /** * Last Picture-in-Picture params applicable to the task. Updated when the app @@ -1000,6 +1004,8 @@ class Task extends WindowContainer<WindowContainer> { origActivity = new ComponentName(info.packageName, info.name); } } + mWindowLayoutAffinity = + info.windowLayout == null ? null : info.windowLayout.windowLayoutAffinity; final int intentFlags = intent == null ? 0 : intent.getFlags(); if ((intentFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { @@ -1929,6 +1935,7 @@ class Task extends WindowContainer<WindowContainer> { // TODO: Should also take care of Pip mode changes here. saveLaunchingStateIfNeeded(); + updateTaskOrganizerState(false /* forceUpdate */); } /** @@ -3408,6 +3415,9 @@ class Task extends WindowContainer<WindowContainer> { pw.println(); } } + if (mWindowLayoutAffinity != null) { + pw.print(prefix); pw.print("windowLayoutAffinity="); pw.println(mWindowLayoutAffinity); + } if (voiceSession != null || voiceInteractor != null) { pw.print(prefix); pw.print("VOICE: session=0x"); pw.print(Integer.toHexString(System.identityHashCode(voiceSession))); @@ -3589,6 +3599,9 @@ class Task extends WindowContainer<WindowContainer> { } else if (rootAffinity != null) { out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@"); } + if (mWindowLayoutAffinity != null) { + out.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity); + } out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset)); out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents)); out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode)); @@ -3746,6 +3759,7 @@ class Task extends WindowContainer<WindowContainer> { String affinity = null; String rootAffinity = null; boolean hasRootAffinity = false; + String windowLayoutAffinity = null; boolean rootHasReset = false; boolean autoRemoveRecents = false; boolean askedCompatMode = false; @@ -3798,6 +3812,9 @@ class Task extends WindowContainer<WindowContainer> { rootAffinity = attrValue; hasRootAffinity = true; break; + case ATTR_WINDOW_LAYOUT_AFFINITY: + windowLayoutAffinity = attrValue; + break; case ATTR_ROOTHASRESET: rootHasReset = Boolean.parseBoolean(attrValue); break; @@ -3953,6 +3970,7 @@ class Task extends WindowContainer<WindowContainer> { realActivitySuspended, userSetupComplete, minWidth, minHeight, null /*stack*/); task.mLastNonFullscreenBounds = lastNonFullscreenBounds; task.setBounds(lastNonFullscreenBounds); + task.mWindowLayoutAffinity = windowLayoutAffinity; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { task.addChild(activities.get(activityNdx)); @@ -4018,6 +4036,39 @@ class Task extends WindowContainer<WindowContainer> { // Called on Binder death. void taskOrganizerDied() { mTaskOrganizer = null; + mLastTaskOrganizerWindowingMode = -1; + } + + /** + * Called when the task state changes (ie. from windowing mode change) an the task organizer + * state should also be updated. + * + * @param forceUpdate Updates the task organizer to the one currently specified in the task + * org controller for the task's windowing mode, ignoring the cached + * windowing mode checks. + */ + void updateTaskOrganizerState(boolean forceUpdate) { + if (!isRootTask()) { + return; + } + + final int windowingMode = getWindowingMode(); + if (!forceUpdate && windowingMode == mLastTaskOrganizerWindowingMode) { + // If our windowing mode hasn't actually changed, then just stick + // with our old organizer. This lets us implement the semantic + // where SysUI can continue to manage it's old tasks + // while CTS temporarily takes over the registration. + return; + } + /* + * Different windowing modes may be managed by different task organizers. If + * getTaskOrganizer returns null, we still call setTaskOrganizer to + * make sure we clear it. + */ + final ITaskOrganizer org = + mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode); + setTaskOrganizer(org); + mLastTaskOrganizerWindowingMode = windowingMode; } @Override @@ -4123,8 +4174,16 @@ class Task extends WindowContainer<WindowContainer> { return mMainWindowSizeChangeTransaction; } + void setActivityWindowingMode(int windowingMode) { + PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setWindowingMode, + PooledLambda.__(ActivityRecord.class), windowingMode); + forAllActivities(c); + c.recycle(); + } + @Override long getProtoFieldId() { return TASK; } + } diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 96a9127344e8..e4f10d9840b2 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -30,13 +30,15 @@ import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; +import com.android.internal.os.SomeArgs; + import java.util.ArrayList; class TaskChangeNotificationController { private static final int LOG_STACK_STATE_MSG = 1; private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2; private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3; - private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4; + private static final int NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4; private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6; private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7; private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8; @@ -118,8 +120,10 @@ class TaskChangeNotificationController { l.onActivityUnpinned(); }; - private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> { - l.onPinnedActivityRestartAttempt(m.arg1 != 0); + private final TaskStackConsumer mNotifyActivityRestartAttempt = (l, m) -> { + SomeArgs args = (SomeArgs) m.obj; + l.onActivityRestartAttempt((RunningTaskInfo) args.arg1, args.argi1 != 0, + args.argi2 != 0); }; private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> { @@ -220,8 +224,8 @@ class TaskChangeNotificationController { case NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG: forAllRemoteListeners(mNotifyActivityUnpinned, msg); break; - case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: - forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg); + case NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: + forAllRemoteListeners(mNotifyActivityRestartAttempt, msg); break; case NOTIFY_FORCED_RESIZABLE_MSG: forAllRemoteListeners(mNotifyActivityForcedResizable, msg); @@ -266,6 +270,9 @@ class TaskChangeNotificationController { forAllRemoteListeners(mNotifyTaskFocusChanged, msg); break; } + if (msg.obj instanceof SomeArgs) { + ((SomeArgs) msg.obj).recycle(); + } } } @@ -358,15 +365,18 @@ class TaskChangeNotificationController { /** * Notifies all listeners when an attempt was made to start an an activity that is already - * running in the pinned stack and the activity was not actually started, but the task is - * either brought to the front or a new Intent is delivered to it. + * running, but the task is either brought to the front or a new Intent is delivered to it. */ - void notifyPinnedActivityRestartAttempt(boolean clearedTask) { - mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG); - final Message msg = - mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG, - clearedTask ? 1 : 0, 0); - forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg); + void notifyActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) { + mHandler.removeMessages(NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG); + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = task; + args.argi1 = homeTaskVisible ? 1 : 0; + args.argi2 = clearedTask ? 1 : 0; + final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG, + args); + forAllLocalListeners(mNotifyActivityRestartAttempt, msg); msg.sendToTarget(); } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 4093fe5cb288..9cbc9ee3f9ee 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -105,7 +105,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub TaskOrganizerState(ITaskOrganizer organizer, int windowingMode, - TaskOrganizerState replacing) { + @Nullable TaskOrganizerState replacing) { mOrganizer = organizer; mDeathRecipient = new DeathRecipient(organizer, windowingMode); try { @@ -203,10 +203,27 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { + if (getTaskOrganizer(windowingMode) != null) { + Slog.w(TAG, "Task organizer already exists for windowing mode: " + + windowingMode); + } + final TaskOrganizerState previousState = + mTaskOrganizersForWindowingMode.get(windowingMode); final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode, - mTaskOrganizersForWindowingMode.get(windowingMode)); + previousState); mTaskOrganizersForWindowingMode.put(windowingMode, state); mTaskOrganizerStates.put(organizer.asBinder(), state); + + if (previousState == null) { + // Only in the case where this is the root task organizer for the given + // windowing mode, we add report all existing tasks in that mode to the new + // task organizer. + mService.mRootWindowContainer.forAllTasks((task) -> { + if (task.getWindowingMode() == windowingMode) { + task.updateTaskOrganizerState(true /* forceUpdate */); + } + }); + } } } finally { Binder.restoreCallingIdentity(origId); @@ -553,18 +570,28 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub WindowContainerTransaction.Change c) { int effects = sanitizeAndApplyChange(wc, c); + final Task tr = wc.asTask(); + final SurfaceControl.Transaction t = c.getBoundsChangeTransaction(); if (t != null) { - Task tr = (Task) wc; tr.setMainWindowSizeChangeTransaction(t); } Rect enterPipBounds = c.getEnterPipBounds(); if (enterPipBounds != null) { - Task tr = (Task) wc; mService.mStackSupervisor.updatePictureInPictureMode(tr, enterPipBounds, true); } + + final int windowingMode = c.getWindowingMode(); + if (windowingMode > -1) { + tr.setWindowingMode(windowingMode); + } + final int childWindowingMode = c.getActivityWindowingMode(); + if (childWindowingMode > -1) { + tr.setActivityWindowingMode(childWindowingMode); + } + return effects; } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 669eb7804d41..57d0a335a688 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -41,6 +41,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.util.ArraySet; +import android.util.MathUtils; import android.util.Slog; import android.view.DisplayInfo; import android.view.SurfaceControl; @@ -52,6 +53,7 @@ import com.android.internal.util.ToBooleanFunction; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.function.Consumer; /** * Controls wallpaper windows visibility, ordering, and so on. @@ -75,8 +77,10 @@ class WallpaperController { private float mLastWallpaperY = -1; private float mLastWallpaperXStep = -1; private float mLastWallpaperYStep = -1; + private float mLastWallpaperZoomOut = 0; private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE; private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE; + private final float mMaxWallpaperScale; // This is set when we are waiting for a wallpaper to tell us it is done // changing its scroll position. @@ -191,9 +195,21 @@ class WallpaperController { return false; }; + /** + * @see #computeLastWallpaperZoomOut() + */ + private Consumer<WindowState> mComputeMaxZoomOutFunction = windowState -> { + if (!windowState.mIsWallpaper + && Float.compare(windowState.mWallpaperZoomOut, mLastWallpaperZoomOut) > 0) { + mLastWallpaperZoomOut = windowState.mWallpaperZoomOut; + } + }; + WallpaperController(WindowManagerService service, DisplayContent displayContent) { mService = service; mDisplayContent = displayContent; + mMaxWallpaperScale = service.mContext.getResources() + .getFloat(com.android.internal.R.dimen.config_wallpaperMaxScale); } WindowState getWallpaperTarget() { @@ -325,20 +341,30 @@ class WallpaperController { rawChanged = true; } - boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset); + if (Float.compare(wallpaperWin.mWallpaperZoomOut, mLastWallpaperZoomOut) != 0) { + wallpaperWin.mWallpaperZoomOut = mLastWallpaperZoomOut; + rawChanged = true; + } + + boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset, + wallpaperWin.mShouldScaleWallpaper + ? zoomOutToScale(wallpaperWin.mWallpaperZoomOut) : 1); if (rawChanged && (wallpaperWin.mAttrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) { try { if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset " + wallpaperWin + " x=" + wallpaperWin.mWallpaperX - + " y=" + wallpaperWin.mWallpaperY); + + " y=" + wallpaperWin.mWallpaperY + + " zoom=" + wallpaperWin.mWallpaperZoomOut); if (sync) { mWaitingOnWallpaper = wallpaperWin; } wallpaperWin.mClient.dispatchWallpaperOffsets( wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY, - wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, sync); + wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, + wallpaperWin.mWallpaperZoomOut, sync); + if (sync) { if (mWaitingOnWallpaper != null) { long start = SystemClock.uptimeMillis(); @@ -378,6 +404,20 @@ class WallpaperController { } } + void setWallpaperZoomOut(WindowState window, float zoom) { + if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) { + window.mWallpaperZoomOut = zoom; + updateWallpaperOffsetLocked(window, false); + } + } + + void setShouldZoomOutWallpaper(WindowState window, boolean shouldZoom) { + if (shouldZoom != window.mShouldScaleWallpaper) { + window.mShouldScaleWallpaper = shouldZoom; + updateWallpaperOffsetLocked(window, false); + } + } + void setWindowWallpaperDisplayOffset(WindowState window, int x, int y) { if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y) { window.mWallpaperDisplayOffsetX = x; @@ -420,6 +460,7 @@ class WallpaperController { } else if (changingTarget.mWallpaperY >= 0) { mLastWallpaperY = changingTarget.mWallpaperY; } + computeLastWallpaperZoomOut(); if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX; } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { @@ -593,6 +634,9 @@ class WallpaperController { mLastWallpaperX = mWallpaperTarget.mWallpaperX; mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep; } + if (mWallpaperTarget.mWallpaperZoomOut >= 0) { + mLastWallpaperZoomOut = mWallpaperTarget.mWallpaperZoomOut; + } if (mWallpaperTarget.mWallpaperY >= 0) { mLastWallpaperY = mWallpaperTarget.mWallpaperY; mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; @@ -762,6 +806,23 @@ class WallpaperController { return mTmpTopWallpaper; } + /** + * Each window can request a zoom, example: + * - User is in overview, zoomed out. + * - User also pulls down the shade. + * + * This means that we always have to choose the largest zoom out that we have, otherwise + * we'll have conflicts and break the "depth system" mental model. + */ + private void computeLastWallpaperZoomOut() { + mLastWallpaperZoomOut = 0; + mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true); + } + + private float zoomOutToScale(float zoom) { + return MathUtils.lerp(1, mMaxWallpaperScale, 1 - zoom); + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId()); pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index e8897e1dbbe2..ec90097e75e9 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -275,7 +275,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< RemoteToken mRemoteToken = null; BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine(); - SurfaceControl.Transaction mBLASTSyncTransaction = new SurfaceControl.Transaction(); + SurfaceControl.Transaction mBLASTSyncTransaction; boolean mUsingBLASTSyncTransaction = false; BLASTSyncEngine.TransactionReadyListener mWaitingListener; int mWaitingSyncId; @@ -283,6 +283,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< WindowContainer(WindowManagerService wms) { mWmService = wms; mPendingTransaction = wms.mTransactionFactory.get(); + mBLASTSyncTransaction = wms.mTransactionFactory.get(); mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms); mSurfaceFreezer = new SurfaceFreezer(this, wms); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 42a66adff5e5..ecbbb03a7c94 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2447,10 +2447,6 @@ public class WindowManagerService extends IWindowManager.Stub // of a transaction to avoid artifacts. win.mAnimatingExit = true; } else { - final DisplayContent displayContent = win.getDisplayContent(); - if (displayContent.mInputMethodWindow == win) { - displayContent.setInputMethodWindowLocked(null); - } boolean stopped = win.mActivityRecord != null ? win.mActivityRecord.mAppStopped : true; // We set mDestroying=true so ActivityRecord#notifyAppStopped in-to destroy surfaces // will later actually destroy the surface if we do not do so here. Normally we leave diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 833bb83acb30..32eb932ea0ed 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -53,6 +53,7 @@ import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.os.Build; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.util.ArraySet; @@ -200,6 +201,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** Whether our process is currently running a {@link IRemoteAnimationRunner} */ private boolean mRunningRemoteAnimation; + /** Whether this process is owned by the System UI package. */ + final boolean mIsSysUiPackage; + public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info, String name, int uid, int userId, Object owner, WindowProcessListener listener) { mInfo = info; @@ -210,6 +214,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mListener = listener; mAtm = atm; mDisplayId = INVALID_DISPLAY; + + mIsSysUiPackage = info.packageName.equals( + mAtm.getSysUiServiceComponentLocked().getPackageName()); + onConfigurationChanged(atm.getGlobalConfiguration()); } @@ -1077,6 +1085,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio * always track the configuration of the non-finishing activity last added to the process. */ private void updateActivityConfigurationListener() { + if (mIsSysUiPackage || mUid == Process.SYSTEM_UID) { + // This is a system owned process and should not use an activity config. + // TODO(b/151161907): Remove after support for display-independent (raw) SysUi configs. + return; + } + for (int i = mActivities.size() - 1; i >= 0; i--) { final ActivityRecord activityRecord = mActivities.get(i); if (!activityRecord.finishing && !activityRecord.containsListener(this)) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index fc28cd594a92..c44be4d00a84 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -415,6 +415,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float mHScale=1, mVScale=1; float mLastHScale=1, mLastVScale=1; final Matrix mTmpMatrix = new Matrix(); + final float[] mTmpMatrixArray = new float[9]; private final WindowFrames mWindowFrames = new WindowFrames(); @@ -446,6 +447,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float mWallpaperX = -1; float mWallpaperY = -1; + // If a window showing a wallpaper: the requested zoom out for the + // wallpaper; if a wallpaper window: the currently applied zoom. + float mWallpaperZoomOut = -1; + + // If a wallpaper window: whether the wallpaper should be scaled when zoomed, if set + // to false, mWallpaperZoom will be ignored here and just passed to the WallpaperService. + boolean mShouldScaleWallpaper; + // If a window showing a wallpaper: what fraction of the offset // range corresponds to a full virtual screen. float mWallpaperXStep = -1; @@ -3923,6 +3932,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.println(prefix + "mWallpaperXStep=" + mWallpaperXStep + " mWallpaperYStep=" + mWallpaperYStep); } + if (mWallpaperZoomOut != -1) { + pw.println(prefix + "mWallpaperZoomOut=" + mWallpaperZoomOut); + } if (mWallpaperDisplayOffsetX != Integer.MIN_VALUE || mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { pw.println(prefix + "mWallpaperDisplayOffsetX=" + mWallpaperDisplayOffsetX diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 79dc5366445e..563710b9e41b 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -16,6 +16,12 @@ package com.android.server.wm; +import static android.graphics.Matrix.MSCALE_X; +import static android.graphics.Matrix.MSCALE_Y; +import static android.graphics.Matrix.MSKEW_X; +import static android.graphics.Matrix.MSKEW_Y; +import static android.graphics.Matrix.MTRANS_X; +import static android.graphics.Matrix.MTRANS_Y; import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; import static android.view.WindowManager.LayoutParams.FLAG_SCALED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; @@ -217,6 +223,10 @@ class WindowStateAnimator { int mXOffset = 0; int mYOffset = 0; + // A scale factor for the surface contents, that will be applied from the center of the visible + // region. + float mWallpaperScale = 1f; + /** * A flag to determine if the WSA needs to offset its position to compensate for the stack's * position update before the WSA surface has resized. @@ -1034,7 +1044,12 @@ class WindowStateAnimator { } } } - mSurfaceController.setPositionInTransaction(xOffset, yOffset, recoveringMemory); + if (!mIsWallpaper) { + mSurfaceController.setPositionInTransaction(xOffset, yOffset, recoveringMemory); + } else { + setWallpaperPositionAndScale( + xOffset, yOffset, mWallpaperScale, recoveringMemory); + } } } @@ -1051,11 +1066,15 @@ class WindowStateAnimator { if (!w.mSeamlesslyRotated) { - applyCrop(clipRect, recoveringMemory); - mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale, - mDtDx * w.mVScale * mExtraVScale, - mDtDy * w.mHScale * mExtraHScale, - mDsDy * w.mVScale * mExtraVScale, recoveringMemory); + if (!mIsWallpaper) { + applyCrop(clipRect, recoveringMemory); + mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale, + mDtDx * w.mVScale * mExtraVScale, + mDtDy * w.mHScale * mExtraHScale, + mDsDy * w.mVScale * mExtraVScale, recoveringMemory); + } else { + setWallpaperPositionAndScale(mXOffset, mYOffset, mWallpaperScale, recoveringMemory); + } } if (mSurfaceResized) { @@ -1202,18 +1221,18 @@ class WindowStateAnimator { mSurfaceController.setTransparentRegionHint(region); } - boolean setWallpaperOffset(int dx, int dy) { - if (mXOffset == dx && mYOffset == dy) { + boolean setWallpaperOffset(int dx, int dy, float scale) { + if (mXOffset == dx && mYOffset == dy && Float.compare(mWallpaperScale, scale) == 0) { return false; } mXOffset = dx; mYOffset = dy; + mWallpaperScale = scale; try { if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset"); mService.openSurfaceTransaction(); - mSurfaceController.setPositionInTransaction(dx, dy, false); - applyCrop(null, false); + setWallpaperPositionAndScale(dx, dy, scale, false); } catch (RuntimeException e) { Slog.w(TAG, "Error positioning surface of " + mWin + " pos=(" + dx + "," + dy + ")", e); @@ -1225,6 +1244,27 @@ class WindowStateAnimator { } } + private void setWallpaperPositionAndScale(int dx, int dy, float scale, + boolean recoveringMemory) { + DisplayInfo displayInfo = mWin.getDisplayInfo(); + Matrix matrix = mWin.mTmpMatrix; + matrix.setTranslate(dx, dy); + matrix.postScale(scale, scale, displayInfo.logicalWidth / 2f, + displayInfo.logicalHeight / 2f); + matrix.getValues(mWin.mTmpMatrixArray); + matrix.reset(); + + mSurfaceController.setPositionInTransaction(mWin.mTmpMatrixArray[MTRANS_X], + mWin.mTmpMatrixArray[MTRANS_Y], recoveringMemory); + mSurfaceController.setMatrixInTransaction( + mDsDx * mWin.mTmpMatrixArray[MSCALE_X] * mWin.mHScale * mExtraHScale, + mDtDx * mWin.mTmpMatrixArray[MSKEW_Y] * mWin.mVScale * mExtraVScale, + mDtDy * mWin.mTmpMatrixArray[MSKEW_X] * mWin.mHScale * mExtraHScale, + mDsDy * mWin.mTmpMatrixArray[MSCALE_Y] * mWin.mVScale * mExtraVScale, + recoveringMemory); + applyCrop(null, recoveringMemory); + } + /** * Try to change the pixel format without recreating the surface. This * will be common in the case of changing from PixelFormat.OPAQUE to diff --git a/services/core/jni/com_android_server_tv_GamepadKeys.h b/services/core/jni/com_android_server_tv_GamepadKeys.h new file mode 100644 index 000000000000..11fc9031da3b --- /dev/null +++ b/services/core/jni/com_android_server_tv_GamepadKeys.h @@ -0,0 +1,79 @@ +#ifndef ANDROIDTVREMOTE_SERVICE_JNI_GAMEPAD_KEYS_H_ +#define ANDROIDTVREMOTE_SERVICE_JNI_GAMEPAD_KEYS_H_ + +#include <linux/input.h> + +namespace android { + +// Follows the W3 spec for gamepad buttons and their corresponding mapping into +// Linux keycodes. Note that gamepads are generally not very well standardized +// and various controllers will result in different buttons. This mapping tries +// to be reasonable. +// +// W3 Button spec: https://www.w3.org/TR/gamepad/#remapping +// +// Standard gamepad keycodes are added plus 2 additional buttons (e.g. Stadia +// has "Assistant" and "Share", PS4 has the touchpad button). +// +// To generate this list, PS4, XBox, Stadia and Nintendo Switch Pro were tested. +static const int GAMEPAD_KEY_CODES[19] = { + // Right-side buttons. A/B/X/Y or circle/triangle/square/X or similar + BTN_A, // "South", A, GAMEPAD and SOUTH have the same constant + BTN_B, // "East", BTN_B, BTN_EAST have the same constant + BTN_X, // "West", Note that this maps to X and NORTH in constants + BTN_Y, // "North", Note that this maps to Y and WEST in constants + + BTN_TL, // "Left Bumper" / "L1" - Nintendo sends BTN_WEST instead + BTN_TR, // "Right Bumper" / "R1" - Nintendo sends BTN_Z instead + + // For triggers, gamepads vary: + // - Stadia sends analog values over ABS_GAS/ABS_BRAKE and sends + // TriggerHappy3/4 as digital presses + // - PS4 and Xbox send analog values as ABS_Z/ABS_RZ + // - Nintendo Pro sends BTN_TL/BTN_TR (since bumpers behave differently) + // As placeholders we chose the stadia trigger-happy values since TL/TR are + // sent for bumper button presses + BTN_TRIGGER_HAPPY4, // "Left Trigger" / "L2" + BTN_TRIGGER_HAPPY3, // "Right Trigger" / "R2" + + BTN_SELECT, // "Select/Back". Often "options" or similar + BTN_START, // "Start/forward". Often "hamburger" icon + + BTN_THUMBL, // "Left Joystick Pressed" + BTN_THUMBR, // "Right Joystick Pressed" + + // For DPads, gamepads generally only send axis changes + // on ABS_HAT0X and ABS_HAT0Y. + KEY_UP, // "Digital Pad up" + KEY_DOWN, // "Digital Pad down" + KEY_LEFT, // "Digital Pad left" + KEY_RIGHT, // "Digital Pad right" + + BTN_MODE, // "Main button" (Stadia/PS/XBOX/Home) + + BTN_TRIGGER_HAPPY1, // Extra button: "Assistant" for Stadia + BTN_TRIGGER_HAPPY2, // Extra button: "Share" for Stadia +}; + +// Defines information for an axis. +struct Axis { + int number; + int rangeMin; + int rangeMax; +}; + +// List of all axes supported by a gamepad +static const Axis GAMEPAD_AXES[] = { + {ABS_X, 0, 254}, // Left joystick X + {ABS_Y, 0, 254}, // Left joystick Y + {ABS_RX, 0, 254}, // Right joystick X + {ABS_RY, 0, 254}, // Right joystick Y + {ABS_Z, 0, 254}, // Left trigger + {ABS_RZ, 0, 254}, // Right trigger + {ABS_HAT0X, -1, 1}, // DPad X + {ABS_HAT0Y, -1, 1}, // DPad Y +}; + +} // namespace android + +#endif // ANDROIDTVREMOTE_SERVICE_JNI_GAMEPAD_KEYS_H_ diff --git a/services/core/jni/com_android_server_tv_TvKeys.h b/services/core/jni/com_android_server_tv_TvKeys.h index 4895f343ad8a..7eacdf6cb253 100644 --- a/services/core/jni/com_android_server_tv_TvKeys.h +++ b/services/core/jni/com_android_server_tv_TvKeys.h @@ -110,4 +110,4 @@ static Key KEYS[] = { } // namespace android -#endif // SERVICE_JNI_KEYS_H_ +#endif // ANDROIDTVREMOTE_SERVICE_JNI_KEYS_H_ diff --git a/services/core/jni/com_android_server_tv_TvUinputBridge.cpp b/services/core/jni/com_android_server_tv_TvUinputBridge.cpp index c832c18517ce..0e96bd7ae47e 100644 --- a/services/core/jni/com_android_server_tv_TvUinputBridge.cpp +++ b/services/core/jni/com_android_server_tv_TvUinputBridge.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "TvRemote-native-uiBridge" +#include "com_android_server_tv_GamepadKeys.h" #include "com_android_server_tv_TvKeys.h" #include "jni.h" @@ -92,27 +93,156 @@ static void unassignSlot(int32_t pointerId) { } } +static const int kInvalidFileDescriptor = -1; + +// Convenience class to manage an opened /dev/uinput device +class UInputDescriptor { +public: + UInputDescriptor() : mFd(kInvalidFileDescriptor) { + memset(&mUinputDescriptor, 0, sizeof(mUinputDescriptor)); + } + + // Auto-closes any open /dev/uinput descriptor unless detached. + ~UInputDescriptor(); + + // Open /dev/uinput and prepare to register + // the device with the given name and unique Id + bool Open(const char* name, const char* uniqueId); + + // Checks if the current file descriptor is valid + bool IsValid() const { return mFd != kInvalidFileDescriptor; } + + void EnableKey(int keyCode); + + void EnableAxesEvents(); + void EnableAxis(int axis, int rangeMin, int rangeMax); + + bool Create(); + + // Detaches from the current file descriptor + // Returns the file descriptor for /dev/uniput + int Detach(); + +private: + int mFd; + struct uinput_user_dev mUinputDescriptor; +}; + +UInputDescriptor::~UInputDescriptor() { + if (mFd != kInvalidFileDescriptor) { + close(mFd); + mFd = kInvalidFileDescriptor; + } +} + +int UInputDescriptor::Detach() { + int fd = mFd; + mFd = kInvalidFileDescriptor; + return fd; +} + +bool UInputDescriptor::Open(const char* name, const char* uniqueId) { + if (IsValid()) { + ALOGE("UInput device already open"); + return false; + } + + mFd = ::open("/dev/uinput", O_WRONLY | O_NDELAY); + if (mFd < 0) { + ALOGE("Cannot open /dev/uinput: %s.", strerror(errno)); + mFd = kInvalidFileDescriptor; + return false; + } + + // write device unique id to the phys property + ioctl(mFd, UI_SET_PHYS, uniqueId); + + memset(&mUinputDescriptor, 0, sizeof(mUinputDescriptor)); + strlcpy(mUinputDescriptor.name, name, UINPUT_MAX_NAME_SIZE); + mUinputDescriptor.id.version = 1; + mUinputDescriptor.id.bustype = BUS_VIRTUAL; + + // All UInput devices we use process keys + ioctl(mFd, UI_SET_EVBIT, EV_KEY); + + return true; +} + +void UInputDescriptor::EnableKey(int keyCode) { + ioctl(mFd, UI_SET_KEYBIT, keyCode); +} + +void UInputDescriptor::EnableAxesEvents() { + ioctl(mFd, UI_SET_EVBIT, EV_ABS); +} + +void UInputDescriptor::EnableAxis(int axis, int rangeMin, int rangeMax) { + if ((axis < 0) || (axis >= NELEM(mUinputDescriptor.absmin))) { + ALOGE("Invalid axis number: %d", axis); + return; + } + + if (ioctl(mFd, UI_SET_ABSBIT, axis) != 0) { + ALOGE("Failed to set absbit for %d", axis); + } + + mUinputDescriptor.absmin[axis] = rangeMin; + mUinputDescriptor.absmax[axis] = rangeMax; + mUinputDescriptor.absfuzz[axis] = 0; + mUinputDescriptor.absflat[axis] = 0; +} + +bool UInputDescriptor::Create() { + // register the input device + if (write(mFd, &mUinputDescriptor, sizeof(mUinputDescriptor)) != sizeof(mUinputDescriptor)) { + ALOGE("Cannot write uinput_user_dev to fd %d: %s.", mFd, strerror(errno)); + return false; + } + + if (ioctl(mFd, UI_DEV_CREATE) != 0) { + ALOGE("Unable to create uinput device: %s.", strerror(errno)); + return false; + } + + ALOGV("Created uinput device, fd=%d.", mFd); + + return true; +} + class NativeConnection { public: + enum class ConnectionType { + kRemoteDevice, + kGamepadDevice, + }; + ~NativeConnection(); static NativeConnection* open(const char* name, const char* uniqueId, int32_t width, int32_t height, int32_t maxPointerId); + static NativeConnection* openGamepad(const char* name, const char* uniqueId); + void sendEvent(int32_t type, int32_t code, int32_t value); int32_t getMaxPointers() const { return mMaxPointers; } + ConnectionType getType() const { return mType; } + + bool IsGamepad() const { return getType() == ConnectionType::kGamepadDevice; } + + bool IsRemote() const { return getType() == ConnectionType::kRemoteDevice; } + private: - NativeConnection(int fd, int32_t maxPointers); + NativeConnection(int fd, int32_t maxPointers, ConnectionType type); const int mFd; const int32_t mMaxPointers; + const ConnectionType mType; }; -NativeConnection::NativeConnection(int fd, int32_t maxPointers) : - mFd(fd), mMaxPointers(maxPointers) { -} +NativeConnection::NativeConnection(int fd, int32_t maxPointers, ConnectionType type) + : mFd(fd), mMaxPointers(maxPointers), mType(type) {} NativeConnection::~NativeConnection() { ALOGI("Un-Registering uinput device %d.", mFd); @@ -125,44 +255,50 @@ NativeConnection* NativeConnection::open(const char* name, const char* uniqueId, ALOGI("Registering uinput device %s: touch pad size %dx%d, " "max pointers %d.", name, width, height, maxPointers); - int fd = ::open("/dev/uinput", O_WRONLY | O_NDELAY); - if (fd < 0) { - ALOGE("Cannot open /dev/uinput: %s.", strerror(errno)); + initKeysMap(); + + UInputDescriptor descriptor; + if (!descriptor.Open(name, uniqueId)) { return nullptr; } - struct uinput_user_dev uinp; - memset(&uinp, 0, sizeof(struct uinput_user_dev)); - strlcpy(uinp.name, name, UINPUT_MAX_NAME_SIZE); - uinp.id.version = 1; - uinp.id.bustype = BUS_VIRTUAL; + // set the keys mapped + for (size_t i = 0; i < NELEM(KEYS); i++) { + descriptor.EnableKey(KEYS[i].linuxKeyCode); + } - // initialize keymap - initKeysMap(); + if (!descriptor.Create()) { + return nullptr; + } - // write device unique id to the phys property - ioctl(fd, UI_SET_PHYS, uniqueId); + return new NativeConnection(descriptor.Detach(), maxPointers, ConnectionType::kRemoteDevice); +} - // set the keys mapped - ioctl(fd, UI_SET_EVBIT, EV_KEY); - for (size_t i = 0; i < NELEM(KEYS); i++) { - ioctl(fd, UI_SET_KEYBIT, KEYS[i].linuxKeyCode); +NativeConnection* NativeConnection::openGamepad(const char* name, const char* uniqueId) { + ALOGI("Registering uinput device %s: gamepad", name); + + UInputDescriptor descriptor; + if (!descriptor.Open(name, uniqueId)) { + return nullptr; } - // register the input device - if (write(fd, &uinp, sizeof(uinp)) != sizeof(uinp)) { - ALOGE("Cannot write uinput_user_dev to fd %d: %s.", fd, strerror(errno)); - close(fd); - return NULL; + // set the keys mapped for gamepads + for (size_t i = 0; i < NELEM(GAMEPAD_KEY_CODES); i++) { + descriptor.EnableKey(GAMEPAD_KEY_CODES[i]); } - if (ioctl(fd, UI_DEV_CREATE) != 0) { - ALOGE("Unable to create uinput device: %s.", strerror(errno)); - close(fd); + + // define the axes that are required + descriptor.EnableAxesEvents(); + for (size_t i = 0; i < NELEM(GAMEPAD_AXES); i++) { + const Axis& axis = GAMEPAD_AXES[i]; + descriptor.EnableAxis(axis.number, axis.rangeMin, axis.rangeMax); + } + + if (!descriptor.Create()) { return nullptr; } - ALOGV("Created uinput device, fd=%d.", fd); - return new NativeConnection(fd, maxPointers); + return new NativeConnection(descriptor.Detach(), 0, ConnectionType::kGamepadDevice); } void NativeConnection::sendEvent(int32_t type, int32_t code, int32_t value) { @@ -174,7 +310,6 @@ void NativeConnection::sendEvent(int32_t type, int32_t code, int32_t value) { write(mFd, &iev, sizeof(iev)); } - static jlong nativeOpen(JNIEnv* env, jclass clazz, jstring nameStr, jstring uniqueIdStr, jint width, jint height, jint maxPointers) { @@ -186,6 +321,14 @@ static jlong nativeOpen(JNIEnv* env, jclass clazz, return reinterpret_cast<jlong>(connection); } +static jlong nativeGamepadOpen(JNIEnv* env, jclass clazz, jstring nameStr, jstring uniqueIdStr) { + ScopedUtfChars name(env, nameStr); + ScopedUtfChars uniqueId(env, uniqueIdStr); + + NativeConnection* connection = NativeConnection::openGamepad(name.c_str(), uniqueId.c_str()); + return reinterpret_cast<jlong>(connection); +} + static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) { NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr); delete connection; @@ -194,6 +337,12 @@ static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) { static void nativeSendKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyCode, jboolean down) { int32_t code = getLinuxKeyCode(keyCode); NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr); + + if (connection->IsGamepad()) { + ALOGE("Invalid key even for a gamepad - need to send gamepad events"); + return; + } + if (code != KEY_UNKNOWN) { connection->sendEvent(EV_KEY, code, down ? 1 : 0); } else { @@ -201,10 +350,44 @@ static void nativeSendKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyCode, jb } } +static void nativeSendGamepadKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyIndex, + jboolean down) { + NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr); + + if (!connection->IsGamepad()) { + ALOGE("Invalid gamepad key for non-gamepad device"); + return; + } + + if ((keyIndex < 0) || (keyIndex >= NELEM(GAMEPAD_KEY_CODES))) { + ALOGE("Invalid gamepad key index: %d", keyIndex); + return; + } + + connection->sendEvent(EV_KEY, GAMEPAD_KEY_CODES[keyIndex], down ? 1 : 0); +} + +static void nativeSendGamepadAxisValue(JNIEnv* env, jclass clazz, jlong ptr, jint axis, + jint value) { + NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr); + + if (!connection->IsGamepad()) { + ALOGE("Invalid axis send for non-gamepad device"); + return; + } + + connection->sendEvent(EV_ABS, axis, value); +} + static void nativeSendPointerDown(JNIEnv* env, jclass clazz, jlong ptr, jint pointerId, jint x, jint y) { NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr); + if (connection->IsGamepad()) { + ALOGE("Invalid pointer down event for a gamepad."); + return; + } + int32_t slot = findSlot(pointerId); if (slot == SLOT_UNKNOWN) { slot = assignSlot(pointerId); @@ -221,6 +404,11 @@ static void nativeSendPointerUp(JNIEnv* env, jclass clazz, jlong ptr, jint pointerId) { NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr); + if (connection->IsGamepad()) { + ALOGE("Invalid pointer up event for a gamepad."); + return; + } + int32_t slot = findSlot(pointerId); if (slot != SLOT_UNKNOWN) { connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot); @@ -238,17 +426,34 @@ static void nativeClear(JNIEnv* env, jclass clazz, jlong ptr) { NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr); // Clear keys. - for (size_t i = 0; i < NELEM(KEYS); i++) { - connection->sendEvent(EV_KEY, KEYS[i].linuxKeyCode, 0); - } + if (connection->IsRemote()) { + for (size_t i = 0; i < NELEM(KEYS); i++) { + connection->sendEvent(EV_KEY, KEYS[i].linuxKeyCode, 0); + } + + // Clear pointers. + int32_t slot = SLOT_UNKNOWN; + for (int32_t i = 0; i < connection->getMaxPointers(); i++) { + slot = findSlot(i); + if (slot != SLOT_UNKNOWN) { + connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot); + connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); + } + } + } else { + for (size_t i = 0; i < NELEM(GAMEPAD_KEY_CODES); i++) { + connection->sendEvent(EV_KEY, GAMEPAD_KEY_CODES[i], 0); + } - // Clear pointers. - int32_t slot = SLOT_UNKNOWN; - for (int32_t i = 0; i < connection->getMaxPointers(); i++) { - slot = findSlot(i); - if (slot != SLOT_UNKNOWN) { - connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot); - connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); + for (size_t i = 0; i < NELEM(GAMEPAD_AXES); i++) { + const Axis& axis = GAMEPAD_AXES[i]; + if ((axis.number == ABS_Z) || (axis.number == ABS_RZ)) { + // Mark triggers unpressed + connection->sendEvent(EV_ABS, axis.number, 0); + } else { + // Joysticks and dpad rests on center + connection->sendEvent(EV_ABS, axis.number, (axis.rangeMin + axis.rangeMax) / 2); + } } } @@ -261,20 +466,16 @@ static void nativeClear(JNIEnv* env, jclass clazz, jlong ptr) { */ static JNINativeMethod gUinputBridgeMethods[] = { - { "nativeOpen", "(Ljava/lang/String;Ljava/lang/String;III)J", - (void*)nativeOpen }, - { "nativeClose", "(J)V", - (void*)nativeClose }, - { "nativeSendKey", "(JIZ)V", - (void*)nativeSendKey }, - { "nativeSendPointerDown", "(JIII)V", - (void*)nativeSendPointerDown }, - { "nativeSendPointerUp", "(JI)V", - (void*)nativeSendPointerUp }, - { "nativeClear", "(J)V", - (void*)nativeClear }, - { "nativeSendPointerSync", "(J)V", - (void*)nativeSendPointerSync }, + {"nativeOpen", "(Ljava/lang/String;Ljava/lang/String;III)J", (void*)nativeOpen}, + {"nativeGamepadOpen", "(Ljava/lang/String;Ljava/lang/String;)J", (void*)nativeGamepadOpen}, + {"nativeClose", "(J)V", (void*)nativeClose}, + {"nativeSendKey", "(JIZ)V", (void*)nativeSendKey}, + {"nativeSendPointerDown", "(JIII)V", (void*)nativeSendPointerDown}, + {"nativeSendPointerUp", "(JI)V", (void*)nativeSendPointerUp}, + {"nativeClear", "(J)V", (void*)nativeClear}, + {"nativeSendPointerSync", "(J)V", (void*)nativeSendPointerSync}, + {"nativeSendGamepadKey", "(JIZ)V", (void*)nativeSendGamepadKey}, + {"nativeSendGamepadAxisValue", "(JII)V", (void*)nativeSendGamepadAxisValue}, }; int register_android_server_tv_TvUinputBridge(JNIEnv* env) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 312d2d2e2ac2..6f41631a8b88 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -86,6 +86,7 @@ import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.provider.Settings.Global.PRIVATE_DNS_MODE; import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; import static android.provider.Telephony.Carriers.DPC_URI; @@ -1198,7 +1199,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Whether the admin explicitly requires personal apps to be suspended boolean mSuspendPersonalApps = false; // Maximum time the profile owned by this admin can be off. - long mProfileMaximumTimeOff = 0; + long mProfileMaximumTimeOffMillis = 0; // Time by which the profile should be turned on according to System.currentTimeMillis(). long mProfileOffDeadline = 0; @@ -1439,10 +1440,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mSuspendPersonalApps) { writeAttributeValueToXml(out, TAG_SUSPEND_PERSONAL_APPS, mSuspendPersonalApps); } - if (mProfileMaximumTimeOff != 0) { - writeAttributeValueToXml(out, TAG_PROFILE_MAXIMUM_TIME_OFF, mProfileMaximumTimeOff); + if (mProfileMaximumTimeOffMillis != 0) { + writeAttributeValueToXml(out, TAG_PROFILE_MAXIMUM_TIME_OFF, + mProfileMaximumTimeOffMillis); } - if (mProfileMaximumTimeOff != 0) { + if (mProfileMaximumTimeOffMillis != 0) { writeAttributeValueToXml(out, TAG_PROFILE_OFF_DEADLINE, mProfileOffDeadline); } if (!TextUtils.isEmpty(mAlwaysOnVpnPackage)) { @@ -1691,7 +1693,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mSuspendPersonalApps = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_PROFILE_MAXIMUM_TIME_OFF.equals(tag)) { - mProfileMaximumTimeOff = + mProfileMaximumTimeOffMillis = Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) { mProfileOffDeadline = @@ -1929,8 +1931,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.println(mCrossProfilePackages); pw.print("mSuspendPersonalApps="); pw.println(mSuspendPersonalApps); - pw.print("mProfileMaximumTimeOff="); - pw.println(mProfileMaximumTimeOff); + pw.print("mProfileMaximumTimeOffMillis="); + pw.println(mProfileMaximumTimeOffMillis); pw.print("mProfileOffDeadline="); pw.println(mProfileOffDeadline); pw.print("mAlwaysOnVpnPackage="); @@ -5874,6 +5876,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void enforceNetworkStackOrProfileOrDeviceOwner(ComponentName who) { + if (mContext.checkCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK) + == PackageManager.PERMISSION_GRANTED) { + return; + } + enforceProfileOrDeviceOwner(who); + } + private void enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(ComponentName who) { synchronized (getLockObject()) { getActiveAdminForCallerLocked( @@ -6870,7 +6880,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException { - enforceProfileOrDeviceOwner(admin); + enforceNetworkStackOrProfileOrDeviceOwner(admin); final int userId = mInjector.userHandleGetCallingUserId(); return mInjector.binderWithCleanCallingIdentity( @@ -7786,7 +7796,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * Set whether auto time is enabled on the device. */ @Override - public void setAutoTime(ComponentName who, boolean enabled) { + public void setAutoTimeEnabled(ComponentName who, boolean enabled) { if (!mHasFeature) { return; } @@ -7807,7 +7817,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * Returns whether auto time is used on the device or not. */ @Override - public boolean getAutoTime(ComponentName who) { + public boolean getAutoTimeEnabled(ComponentName who) { if (!mHasFeature) { return false; } @@ -7821,7 +7831,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * Set whether auto time zone is enabled on the device. */ @Override - public void setAutoTimeZone(ComponentName who, boolean enabled) { + public void setAutoTimeZoneEnabled(ComponentName who, boolean enabled) { if (!mHasFeature) { return; } @@ -7842,7 +7852,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * Returns whether auto time zone is used on the device or not. */ @Override - public boolean getAutoTimeZone(ComponentName who) { + public boolean getAutoTimeZoneEnabled(ComponentName who) { if (!mHasFeature) { return false; } @@ -15716,18 +15726,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } boolean shouldSaveSettings = false; if (profileOwner.mProfileOffDeadline != 0 - && (profileOwner.mProfileMaximumTimeOff == 0 || unlocked)) { + && (profileOwner.mProfileMaximumTimeOffMillis == 0 || unlocked)) { // There is a deadline but either there is no policy or the profile is unlocked -> clear // the deadline. Slog.i(LOG_TAG, "Profile off deadline is reset to zero"); profileOwner.mProfileOffDeadline = 0; shouldSaveSettings = true; } else if (profileOwner.mProfileOffDeadline == 0 - && (profileOwner.mProfileMaximumTimeOff != 0 && !unlocked)) { + && (profileOwner.mProfileMaximumTimeOffMillis != 0 && !unlocked)) { // There profile is locked and there is a policy, but the deadline is not set -> set the // deadline. Slog.i(LOG_TAG, "Profile off deadline is set."); - profileOwner.mProfileOffDeadline = now + profileOwner.mProfileMaximumTimeOff; + profileOwner.mProfileOffDeadline = now + profileOwner.mProfileMaximumTimeOffMillis; shouldSaveSettings = true; } @@ -15828,7 +15838,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public void setManagedProfileMaximumTimeOff(ComponentName who, long timeoutMs) { + public void setManagedProfileMaximumTimeOff(ComponentName who, long timeoutMillis) { final int userId = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { final ActiveAdmin admin = getActiveAdminForCallerLocked(who, @@ -15837,10 +15847,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // DO shouldn't be able to use this method. enforceProfileOwnerOfOrganizationOwnedDevice(admin); enforceHandlesCheckPolicyComplianceIntent(userId, admin.info.getPackageName()); - if (admin.mProfileMaximumTimeOff == timeoutMs) { + if (admin.mProfileMaximumTimeOffMillis == timeoutMillis) { return; } - admin.mProfileMaximumTimeOff = timeoutMs; + admin.mProfileMaximumTimeOffMillis = timeoutMillis; saveSettingsLocked(userId); } @@ -15850,7 +15860,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF) .setAdmin(who) - .setTimePeriod(timeoutMs) + .setTimePeriod(timeoutMillis) .write(); } @@ -15874,7 +15884,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { false /* parent */); // DO shouldn't be able to use this method. enforceProfileOwnerOfOrganizationOwnedDevice(admin); - return admin.mProfileMaximumTimeOff; + return admin.mProfileMaximumTimeOffMillis; } } diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 2426e8f29dca..cccd01339177 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -28,6 +28,7 @@ #include <androidfw/ZipFileRO.h> #include <androidfw/ZipUtils.h> #include <binder/BinderService.h> +#include <binder/Nullable.h> #include <binder/ParcelFileDescriptor.h> #include <binder/Status.h> #include <sys/stat.h> @@ -917,19 +918,23 @@ std::vector<std::string> IncrementalService::listFiles(StorageId storage) const } bool IncrementalService::startLoading(StorageId storage) const { - const auto ifs = getIfs(storage); - if (!ifs) { - return false; - } - std::unique_lock l(ifs->lock); - if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) { - if (ifs->dataLoaderReady.wait_for(l, Seconds(5)) == std::cv_status::timeout) { - LOG(ERROR) << "Timeout waiting for data loader to be ready"; + { + std::unique_lock l(mLock); + const auto& ifs = getIfsLocked(storage); + if (!ifs) { return false; } + if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) { + ifs->dataLoaderStartRequested = true; + return true; + } } + return startDataLoader(storage); +} + +bool IncrementalService::startDataLoader(MountId mountId) const { sp<IDataLoader> dataloader; - auto status = mDataLoaderManager->getDataLoader(ifs->mountId, &dataloader); + auto status = mDataLoaderManager->getDataLoader(mountId, &dataloader); if (!status.isOk()) { return false; } @@ -1065,15 +1070,9 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, } return true; // eventually... } - if (base::GetBoolProperty("incremental.skip_loader", false)) { - LOG(INFO) << "Skipped data loader because of incremental.skip_loader property"; - std::unique_lock l(ifs.lock); - ifs.savedDataLoaderParams.reset(); - return true; - } std::unique_lock l(ifs.lock); - if (ifs.dataLoaderStatus == IDataLoaderStatusListener::DATA_LOADER_CREATED) { + if (ifs.dataLoaderStatus != -1) { LOG(INFO) << "Skipped data loader preparation because it already exists"; return true; } @@ -1085,7 +1084,7 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, return false; } FileSystemControlParcel fsControlParcel; - fsControlParcel.incremental = std::make_unique<IncrementalFileSystemControlParcel>(); + fsControlParcel.incremental = aidl::make_nullable<IncrementalFileSystemControlParcel>(); fsControlParcel.incremental->cmd.reset(base::unique_fd(::dup(ifs.control.cmd))); fsControlParcel.incremental->pendingReads.reset( base::unique_fd(::dup(ifs.control.pendingReads))); @@ -1226,30 +1225,42 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange externalListener->onStatusChanged(mountId, newStatus); } - std::unique_lock l(incrementalService.mLock); - const auto& ifs = incrementalService.getIfsLocked(mountId); - if (!ifs) { - LOG(WARNING) << "Received data loader status " << int(newStatus) << " for unknown mount " - << mountId; - return binder::Status::ok(); + bool startRequested = false; + { + std::unique_lock l(incrementalService.mLock); + const auto& ifs = incrementalService.getIfsLocked(mountId); + if (!ifs) { + LOG(WARNING) << "Received data loader status " << int(newStatus) << " for unknown mount " + << mountId; + return binder::Status::ok(); + } + ifs->dataLoaderStatus = newStatus; + + if (newStatus == IDataLoaderStatusListener::DATA_LOADER_DESTROYED) { + ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STOPPED; + incrementalService.deleteStorageLocked(*ifs, std::move(l)); + return binder::Status::ok(); + } + + startRequested = ifs->dataLoaderStartRequested; } - ifs->dataLoaderStatus = newStatus; + switch (newStatus) { case IDataLoaderStatusListener::DATA_LOADER_NO_CONNECTION: { // TODO(b/150411019): handle data loader connection loss break; } case IDataLoaderStatusListener::DATA_LOADER_CONNECTION_OK: { - ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STARTED; + // TODO(b/150411019): handle data loader connection loss break; } case IDataLoaderStatusListener::DATA_LOADER_CREATED: { - ifs->dataLoaderReady.notify_one(); + if (startRequested) { + incrementalService.startDataLoader(mountId); + } break; } case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: { - ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STOPPED; - incrementalService.deleteStorageLocked(*ifs, std::move(l)); break; } case IDataLoaderStatusListener::DATA_LOADER_STARTED: { diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 8ff441b5a276..406b32e51044 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -170,7 +170,7 @@ private: std::optional<DataLoaderParamsParcel> savedDataLoaderParams; std::atomic<int> nextStorageDirNo{0}; std::atomic<int> dataLoaderStatus = -1; - std::condition_variable dataLoaderReady; + bool dataLoaderStartRequested = false; TimePoint connectionLostTime = TimePoint(); const IncrementalService& incrementalService; @@ -208,6 +208,8 @@ private: bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params = nullptr, const DataLoaderStatusListener* externalListener = nullptr); + bool startDataLoader(MountId mountId) const; + BindPathMap::const_iterator findStorageLocked(std::string_view path) const; StorageId findStorageId(std::string_view path) const; diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java index 8481e5b916a6..28e3d4b5b744 100644 --- a/services/people/java/com/android/server/people/data/ConversationStore.java +++ b/services/people/java/com/android/server/people/data/ConversationStore.java @@ -89,25 +89,21 @@ class ConversationStore { * Loads conversations from disk to memory in a background thread. This should be called * after the device powers on and the user has been unlocked. */ - @MainThread - void loadConversationsFromDisk() { - mScheduledExecutorService.execute(() -> { - synchronized (this) { - ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter = - getConversationInfosProtoDiskReadWriter(); - if (conversationInfosProtoDiskReadWriter == null) { - return; - } - List<ConversationInfo> conversationsOnDisk = - conversationInfosProtoDiskReadWriter.read(CONVERSATIONS_FILE_NAME); - if (conversationsOnDisk == null) { - return; - } - for (ConversationInfo conversationInfo : conversationsOnDisk) { - updateConversationsInMemory(conversationInfo); - } - } - }); + @WorkerThread + synchronized void loadConversationsFromDisk() { + ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter = + getConversationInfosProtoDiskReadWriter(); + if (conversationInfosProtoDiskReadWriter == null) { + return; + } + List<ConversationInfo> conversationsOnDisk = + conversationInfosProtoDiskReadWriter.read(CONVERSATIONS_FILE_NAME); + if (conversationsOnDisk == null) { + return; + } + for (ConversationInfo conversationInfo : conversationsOnDisk) { + updateConversationsInMemory(conversationInfo); + } } /** diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index 4e0afc525383..7085f96dd31a 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -28,6 +28,7 @@ import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -87,13 +88,13 @@ public class DataManager { private static final String TAG = "DataManager"; - private static final long QUERY_EVENTS_MAX_AGE_MS = DateUtils.DAY_IN_MILLIS; + private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS; private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L; private final Context mContext; private final Injector mInjector; - private final ScheduledExecutorService mUsageStatsQueryExecutor; - private final ScheduledExecutorService mDiskReadWriterExecutor; + private final ScheduledExecutorService mScheduledExecutor; + private final Object mLock = new Object(); private final SparseArray<UserData> mUserDataArray = new SparseArray<>(); private final SparseArray<BroadcastReceiver> mBroadcastReceivers = new SparseArray<>(); @@ -118,8 +119,7 @@ public class DataManager { DataManager(Context context, Injector injector) { mContext = context; mInjector = injector; - mUsageStatsQueryExecutor = mInjector.createScheduledExecutor(); - mDiskReadWriterExecutor = mInjector.createScheduledExecutor(); + mScheduledExecutor = mInjector.createScheduledExecutor(); } /** Initialization. Called when the system services are up running. */ @@ -138,103 +138,56 @@ public class DataManager { /** This method is called when a user is unlocked. */ public void onUserUnlocked(int userId) { - UserData userData = mUserDataArray.get(userId); - if (userData == null) { - userData = new UserData(userId, mDiskReadWriterExecutor); - mUserDataArray.put(userId, userData); - } - userData.setUserUnlocked(); - updateDefaultDialer(userData); - updateDefaultSmsApp(userData); - - ScheduledFuture<?> scheduledFuture = mUsageStatsQueryExecutor.scheduleAtFixedRate( - new UsageStatsQueryRunnable(userId), 1L, USAGE_STATS_QUERY_INTERVAL_SEC, - TimeUnit.SECONDS); - mUsageStatsQueryFutures.put(userId, scheduledFuture); - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED); - intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); - BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId); - mBroadcastReceivers.put(userId, broadcastReceiver); - mContext.registerReceiverAsUser( - broadcastReceiver, UserHandle.of(userId), intentFilter, null, null); - - ContentObserver contactsContentObserver = new ContactsContentObserver( - BackgroundThread.getHandler()); - mContactsContentObservers.put(userId, contactsContentObserver); - mContext.getContentResolver().registerContentObserver( - Contacts.CONTENT_URI, /* notifyForDescendants= */ true, - contactsContentObserver, userId); - - NotificationListener notificationListener = new NotificationListener(); - mNotificationListeners.put(userId, notificationListener); - try { - notificationListener.registerAsSystemService(mContext, - new ComponentName(mContext, getClass()), userId); - } catch (RemoteException e) { - // Should never occur for local calls. - } - - PackageMonitor packageMonitor = new PerUserPackageMonitor(); - packageMonitor.register(mContext, null, UserHandle.of(userId), true); - mPackageMonitors.put(userId, packageMonitor); - - if (userId == UserHandle.USER_SYSTEM) { - // The call log and MMS/SMS messages are shared across user profiles. So only need to - // register the content observers once for the primary user. - // TODO: Register observers after the conversations and events being loaded from disk. - mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler()); - mContext.getContentResolver().registerContentObserver( - CallLog.CONTENT_URI, /* notifyForDescendants= */ true, - mCallLogContentObserver, UserHandle.USER_SYSTEM); - - mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler()); - mContext.getContentResolver().registerContentObserver( - MmsSms.CONTENT_URI, /* notifyForDescendants= */ false, - mMmsSmsContentObserver, UserHandle.USER_SYSTEM); + synchronized (mLock) { + UserData userData = mUserDataArray.get(userId); + if (userData == null) { + userData = new UserData(userId, mScheduledExecutor); + mUserDataArray.put(userId, userData); + } + userData.setUserUnlocked(); } - - DataMaintenanceService.scheduleJob(mContext, userId); + mScheduledExecutor.execute(() -> setupUser(userId)); } /** This method is called when a user is stopping. */ public void onUserStopping(int userId) { - if (mUserDataArray.indexOfKey(userId) >= 0) { - mUserDataArray.get(userId).setUserStopped(); - } - if (mUsageStatsQueryFutures.indexOfKey(userId) >= 0) { - mUsageStatsQueryFutures.get(userId).cancel(true); - } - if (mBroadcastReceivers.indexOfKey(userId) >= 0) { - mContext.unregisterReceiver(mBroadcastReceivers.get(userId)); - } - if (mContactsContentObservers.indexOfKey(userId) >= 0) { - mContext.getContentResolver().unregisterContentObserver( - mContactsContentObservers.get(userId)); - } - if (mNotificationListeners.indexOfKey(userId) >= 0) { - try { - mNotificationListeners.get(userId).unregisterAsSystemService(); - } catch (RemoteException e) { - // Should never occur for local calls. + synchronized (mLock) { + ContentResolver contentResolver = mContext.getContentResolver(); + if (mUserDataArray.indexOfKey(userId) >= 0) { + mUserDataArray.get(userId).setUserStopped(); } - } - if (mPackageMonitors.indexOfKey(userId) >= 0) { - mPackageMonitors.get(userId).unregister(); - } - if (userId == UserHandle.USER_SYSTEM) { - if (mCallLogContentObserver != null) { - mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver); - mCallLogContentObserver = null; + if (mUsageStatsQueryFutures.indexOfKey(userId) >= 0) { + mUsageStatsQueryFutures.get(userId).cancel(true); } - if (mMmsSmsContentObserver != null) { - mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver); - mCallLogContentObserver = null; + if (mBroadcastReceivers.indexOfKey(userId) >= 0) { + mContext.unregisterReceiver(mBroadcastReceivers.get(userId)); + } + if (mContactsContentObservers.indexOfKey(userId) >= 0) { + contentResolver.unregisterContentObserver(mContactsContentObservers.get(userId)); + } + if (mNotificationListeners.indexOfKey(userId) >= 0) { + try { + mNotificationListeners.get(userId).unregisterAsSystemService(); + } catch (RemoteException e) { + // Should never occur for local calls. + } + } + if (mPackageMonitors.indexOfKey(userId) >= 0) { + mPackageMonitors.get(userId).unregister(); + } + if (userId == UserHandle.USER_SYSTEM) { + if (mCallLogContentObserver != null) { + contentResolver.unregisterContentObserver(mCallLogContentObserver); + mCallLogContentObserver = null; + } + if (mMmsSmsContentObserver != null) { + contentResolver.unregisterContentObserver(mMmsSmsContentObserver); + mCallLogContentObserver = null; + } } - } - DataMaintenanceService.cancelJob(mContext, userId); + DataMaintenanceService.cancelJob(mContext, userId); + } } /** @@ -288,6 +241,9 @@ public class DataManager { return; } UserData userData = getUnlockedUserData(appTarget.getUser().getIdentifier()); + if (userData == null) { + return; + } PackageData packageData = userData.getOrCreatePackageData(appTarget.getPackageName()); String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null; @Event.EventType int eventType = mimeTypeToShareEventType(mimeType); @@ -353,6 +309,68 @@ public class DataManager { userData.restore(payload); } + private void setupUser(@UserIdInt int userId) { + synchronized (mLock) { + UserData userData = getUnlockedUserData(userId); + if (userData == null) { + return; + } + userData.loadUserData(); + + updateDefaultDialer(userData); + updateDefaultSmsApp(userData); + + ScheduledFuture<?> scheduledFuture = mScheduledExecutor.scheduleAtFixedRate( + new UsageStatsQueryRunnable(userId), 1L, USAGE_STATS_QUERY_INTERVAL_SEC, + TimeUnit.SECONDS); + mUsageStatsQueryFutures.put(userId, scheduledFuture); + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED); + intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); + BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId); + mBroadcastReceivers.put(userId, broadcastReceiver); + mContext.registerReceiverAsUser( + broadcastReceiver, UserHandle.of(userId), intentFilter, null, null); + + ContentObserver contactsContentObserver = new ContactsContentObserver( + BackgroundThread.getHandler()); + mContactsContentObservers.put(userId, contactsContentObserver); + mContext.getContentResolver().registerContentObserver( + Contacts.CONTENT_URI, /* notifyForDescendants= */ true, + contactsContentObserver, userId); + + NotificationListener notificationListener = new NotificationListener(); + mNotificationListeners.put(userId, notificationListener); + try { + notificationListener.registerAsSystemService(mContext, + new ComponentName(mContext, getClass()), userId); + } catch (RemoteException e) { + // Should never occur for local calls. + } + + PackageMonitor packageMonitor = new PerUserPackageMonitor(); + packageMonitor.register(mContext, null, UserHandle.of(userId), true); + mPackageMonitors.put(userId, packageMonitor); + + if (userId == UserHandle.USER_SYSTEM) { + // The call log and MMS/SMS messages are shared across user profiles. So only need + // to register the content observers once for the primary user. + mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler()); + mContext.getContentResolver().registerContentObserver( + CallLog.CONTENT_URI, /* notifyForDescendants= */ true, + mCallLogContentObserver, UserHandle.USER_SYSTEM); + + mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler()); + mContext.getContentResolver().registerContentObserver( + MmsSms.CONTENT_URI, /* notifyForDescendants= */ false, + mMmsSmsContentObserver, UserHandle.USER_SYSTEM); + } + + DataMaintenanceService.scheduleJob(mContext, userId); + } + } + private int mimeTypeToShareEventType(String mimeType) { if (mimeType.startsWith("text/")) { return Event.TYPE_SHARE_TEXT; diff --git a/services/people/java/com/android/server/people/data/EventStore.java b/services/people/java/com/android/server/people/data/EventStore.java index 00d4241fc5f7..9cf84c94f0f7 100644 --- a/services/people/java/com/android/server/people/data/EventStore.java +++ b/services/people/java/com/android/server/people/data/EventStore.java @@ -17,9 +17,9 @@ package com.android.server.people.data; import android.annotation.IntDef; -import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.WorkerThread; import android.net.Uri; import android.util.ArrayMap; @@ -90,20 +90,16 @@ class EventStore { * Loads existing {@link EventHistoryImpl}s from disk. This should be called when device powers * on and user is unlocked. */ - @MainThread - void loadFromDisk() { - mScheduledExecutorService.execute(() -> { - synchronized (this) { - for (@EventCategory int category = 0; category < mEventsCategoryDirs.size(); - category++) { - File categoryDir = mEventsCategoryDirs.get(category); - Map<String, EventHistoryImpl> existingEventHistoriesImpl = - EventHistoryImpl.eventHistoriesImplFromDisk(categoryDir, - mScheduledExecutorService); - mEventHistoryMaps.get(category).putAll(existingEventHistoriesImpl); - } - } - }); + @WorkerThread + synchronized void loadFromDisk() { + for (@EventCategory int category = 0; category < mEventsCategoryDirs.size(); + category++) { + File categoryDir = mEventsCategoryDirs.get(category); + Map<String, EventHistoryImpl> existingEventHistoriesImpl = + EventHistoryImpl.eventHistoriesImplFromDisk(categoryDir, + mScheduledExecutorService); + mEventHistoryMaps.get(category).putAll(existingEventHistoriesImpl); + } } /** diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java index 3e4c992ce70b..28837d5bf148 100644 --- a/services/people/java/com/android/server/people/data/PackageData.java +++ b/services/people/java/com/android/server/people/data/PackageData.java @@ -25,6 +25,7 @@ import static com.android.server.people.data.EventStore.CATEGORY_SMS; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.annotation.WorkerThread; import android.content.LocusId; import android.os.FileUtils; import android.text.TextUtils; @@ -77,6 +78,7 @@ public class PackageData { * Returns a map of package directory names as keys and their associated {@link PackageData}. * This should be called when device is powered on and unlocked. */ + @WorkerThread @NonNull static Map<String, PackageData> packagesDataFromDisk(@UserIdInt int userId, @NonNull Predicate<String> isDefaultDialerPredicate, diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java index ed8c595ab42a..429d5b7193ce 100644 --- a/services/people/java/com/android/server/people/data/UserData.java +++ b/services/people/java/com/android/server/people/data/UserData.java @@ -19,6 +19,7 @@ package com.android.server.people.data; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.annotation.WorkerThread; import android.os.Environment; import android.text.TextUtils; import android.util.ArrayMap; @@ -75,12 +76,6 @@ class UserData { void setUserUnlocked() { mIsUnlocked = true; - - // Ensures per user root directory for people data is present, and attempt to load - // data from disk. - mPerUserPeopleDataDir.mkdirs(); - mPackageDataMap.putAll(PackageData.packagesDataFromDisk(mUserId, this::isDefaultDialer, - this::isDefaultSmsApp, mScheduledExecutorService, mPerUserPeopleDataDir)); } void setUserStopped() { @@ -91,6 +86,15 @@ class UserData { return mIsUnlocked; } + @WorkerThread + void loadUserData() { + mPerUserPeopleDataDir.mkdir(); + Map<String, PackageData> packageDataMap = PackageData.packagesDataFromDisk( + mUserId, this::isDefaultDialer, this::isDefaultSmsApp, mScheduledExecutorService, + mPerUserPeopleDataDir); + mPackageDataMap.putAll(packageDataMap); + } + /** * Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise * creates a new instance and returns it. diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index c94bb879f460..5c8220049d09 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -79,6 +79,7 @@ public class RescuePartyTest { private static final String CALLING_PACKAGE2 = "com.package.name2"; private static final String NAMESPACE1 = "namespace1"; private static final String NAMESPACE2 = "namespace2"; + private static final String DISABLE_RESCUE_PARTY_FLAG = "disable_rescue_party"; private MockitoSession mSession; private HashMap<String, String> mSystemSettingsMap; @@ -316,6 +317,13 @@ public class RescuePartyTest { @Test public void testExplicitlyEnablingAndDisablingRescue() { + // mock the DeviceConfig get call to avoid hitting + // android.permission.READ_DEVICE_CONFIG when calling real DeviceConfig. + doReturn(true) + .when(() -> DeviceConfig.getBoolean( + eq(DeviceConfig.NAMESPACE_CONFIGURATION), + eq(DISABLE_RESCUE_PARTY_FLAG), + eq(false))); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, @@ -327,6 +335,22 @@ public class RescuePartyTest { } @Test + public void testDisablingRescueByDeviceConfigFlag() { + doReturn(true) + .when(() -> DeviceConfig.getBoolean( + eq(DeviceConfig.NAMESPACE_CONFIGURATION), + eq(DISABLE_RESCUE_PARTY_FLAG), + eq(false))); + SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); + + assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false); + + // Restore the property value initalized in SetUp() + SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); + } + + @Test public void testHealthCheckLevels() { RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java index 95f4e832cd2d..c45ee7b4d802 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java @@ -55,6 +55,7 @@ import android.system.OsConstants; import android.text.TextUtils; import android.util.Pair; +import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerService; @@ -125,6 +126,8 @@ public class ApplicationExitInfoTest { mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper()); mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal()); mAms.mPackageManagerInt = mPackageManagerInt; + doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent(); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); } @After diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index fc2ae40daca0..053a7984b1a3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -75,8 +75,10 @@ import static org.mockito.Mockito.spy; import android.app.IApplicationThread; import android.app.IServiceConnection; +import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManagerInternal; import android.content.pm.ServiceInfo; import android.os.Build; import android.os.IBinder; @@ -87,6 +89,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; +import com.android.server.LocalServices; import com.android.server.wm.ActivityServiceConnectionsHolder; import com.android.server.wm.ActivityTaskManagerService; import com.android.server.wm.WindowProcessController; @@ -127,6 +130,7 @@ public class MockingOomAdjusterTests { private static final String MOCKAPP5_PROCESSNAME = "test #5"; private static final String MOCKAPP5_PACKAGENAME = "com.android.test.test5"; private static Context sContext; + private static PackageManagerInternal sPackageManagerInternal; private static ActivityManagerService sService; @BeforeClass @@ -134,13 +138,21 @@ public class MockingOomAdjusterTests { sContext = getInstrumentation().getTargetContext(); System.setProperty("dexmaker.share_classloader", "true"); + sPackageManagerInternal = mock(PackageManagerInternal.class); + doReturn(new ComponentName("", "")).when(sPackageManagerInternal) + .getSystemUiServiceComponent(); + LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal); + sService = mock(ActivityManagerService.class); sService.mActivityTaskManager = new ActivityTaskManagerService(sContext); sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper()); + sService.mPackageManagerInt = sPackageManagerInternal; sService.mAtmInternal = spy(sService.mActivityTaskManager.getAtmInternal()); sService.mConstants = new ActivityManagerConstants(sContext, sService, sContext.getMainThreadHandler()); + setFieldValue(ActivityManagerService.class, sService, "mContext", + sContext); ProcessList pr = new ProcessList(); pr.init(sService, new ActiveUids(sService, false), null); setFieldValue(ActivityManagerService.class, sService, "mProcessList", @@ -972,7 +984,7 @@ public class MockingOomAdjusterTests { sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); doReturn(null).when(sService).getTopAppLocked(); - assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, VISIBLE_APP_ADJ, + assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); } diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java index 959dc0525e8f..de6f55ba1053 100644 --- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java @@ -147,7 +147,7 @@ public class AppOpsServiceTest { AndroidPackage mockMyPkg = mock(AndroidPackage.class); when(mockMyPkg.isPrivileged()).thenReturn(false); when(mockMyPkg.getUid()).thenReturn(mMyUid); - when(mockMyPkg.getFeatures()).thenReturn(Collections.emptyList()); + when(mockMyPkg.getAttributions()).thenReturn(Collections.emptyList()); when(mockPackageManagerInternal.getPackage(sMyPackageName)).thenReturn(mockMyPkg); doReturn(mockPackageManagerInternal).when( diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index eb2dd6461071..8be9213fd925 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -251,6 +251,28 @@ public class CompatConfigTest { } @Test + public void testAllowRemoveOverrideNoOverride() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .addLoggingOnlyChangeWithId(2L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("com.some.package") + .build(); + when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt())) + .thenReturn(applicationInfo); + + // Reject all override attempts. + // Force the validator to prevent overriding the change by using a user build. + when(mBuildClassifier.isDebuggableBuild()).thenReturn(false); + when(mBuildClassifier.isFinalBuild()).thenReturn(true); + // Try to remove a non existing override, and it doesn't fail. + assertThat(compatConfig.removeOverride(1234L, "com.some.package")).isFalse(); + assertThat(compatConfig.removeOverride(2L, "com.some.package")).isFalse(); + compatConfig.removePackageOverrides("com.some.package"); + } + + @Test public void testRemovePackageOverride() throws Exception { CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) .addEnabledChangeWithId(1234L) @@ -267,6 +289,49 @@ public class CompatConfigTest { } @Test + public void testEnableTargetSdkChangesForPackage() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(1L) + .addDisabledChangeWithId(2L) + .addTargetSdkChangeWithId(3, 3L) + .addTargetSdkChangeWithId(4, 4L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("foo.bar") + .withTargetSdk(2) + .build(); + + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + + assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1); + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + } + + @Test + public void testDisableTargetSdkChangesForPackage() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(1L) + .addDisabledChangeWithId(2L) + .addTargetSdkChangeWithId(3, 3L) + .addTargetSdkChangeWithId(4, 4L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("foo.bar") + .withTargetSdk(2) + .build(); + + assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1); + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + + assertThat(compatConfig.disableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1); + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + } + + @Test public void testLookupChangeId() throws Exception { CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) .addEnabledChangeWithIdAndName(1234L, "MY_CHANGE") diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 2a6a24eab3d9..7571f0916974 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -3872,79 +3872,80 @@ public class DevicePolicyManagerTest extends DpmTestBase { Settings.System.SCREEN_BRIGHTNESS, "0", DpmMockContext.CALLER_USER_HANDLE); } - public void testSetAutoTimeModifiesSetting() throws Exception { + public void testSetAutoTimeEnabledModifiesSetting() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); - dpm.setAutoTime(admin1, true); + dpm.setAutoTimeEnabled(admin1, true); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1); - dpm.setAutoTime(admin1, false); + dpm.setAutoTimeEnabled(admin1, false); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0); } - public void testSetAutoTimeWithPOOnUser0() throws Exception { + public void testSetAutoTimeEnabledWithPOOnUser0() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupProfileOwnerOnUser0(); - dpm.setAutoTime(admin1, true); + dpm.setAutoTimeEnabled(admin1, true); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1); - dpm.setAutoTime(admin1, false); + dpm.setAutoTimeEnabled(admin1, false); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0); } - public void testSetAutoTimeFailWithPONotOnUser0() throws Exception { + public void testSetAutoTimeEnabledFailWithPONotOnUser0() throws Exception { setupProfileOwner(); - assertExpectException(SecurityException.class, null, () -> dpm.setAutoTime(admin1, false)); + assertExpectException(SecurityException.class, null, + () -> dpm.setAutoTimeEnabled(admin1, false)); verify(getServices().settings, never()).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0); } - public void testSetAutoTimeWithPOOfOrganizationOwnedDevice() throws Exception { + public void testSetAutoTimeEnabledWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); - dpm.setAutoTime(admin1, true); + dpm.setAutoTimeEnabled(admin1, true); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1); - dpm.setAutoTime(admin1, false); + dpm.setAutoTimeEnabled(admin1, false); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0); } - public void testSetAutoTimeZoneModifiesSetting() throws Exception { + public void testSetAutoTimeZoneEnabledModifiesSetting() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); - dpm.setAutoTimeZone(admin1, true); + dpm.setAutoTimeZoneEnabled(admin1, true); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1); - dpm.setAutoTimeZone(admin1, false); + dpm.setAutoTimeZoneEnabled(admin1, false); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0); } - public void testSetAutoTimeZoneWithPOOnUser0() throws Exception { + public void testSetAutoTimeZoneEnabledWithPOOnUser0() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupProfileOwnerOnUser0(); - dpm.setAutoTimeZone(admin1, true); + dpm.setAutoTimeZoneEnabled(admin1, true); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1); - dpm.setAutoTimeZone(admin1, false); + dpm.setAutoTimeZoneEnabled(admin1, false); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0); } - public void testSetAutoTimeZoneFailWithPONotOnUser0() throws Exception { + public void testSetAutoTimeZoneEnabledFailWithPONotOnUser0() throws Exception { setupProfileOwner(); assertExpectException(SecurityException.class, null, - () -> dpm.setAutoTimeZone(admin1, false)); + () -> dpm.setAutoTimeZoneEnabled(admin1, false)); verify(getServices().settings, never()).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0); } - public void testSetAutoTimeZoneWithPOOfOrganizationOwnedDevice() throws Exception { + public void testSetAutoTimeZoneEnabledWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); - dpm.setAutoTimeZone(admin1, true); + dpm.setAutoTimeZoneEnabled(admin1, true); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1); - dpm.setAutoTimeZone(admin1, false); + dpm.setAutoTimeZoneEnabled(admin1, false); verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0); } diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index 3dd150479ddc..81545d4c74ba 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -28,6 +28,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -321,6 +322,11 @@ public class AppIntegrityManagerServiceImplTest { // we cannot check installer cert because it seems to be device specific. assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode()); assertFalse(appInstallMetadata.isPreInstalled()); + // Asserting source stamp not present. + assertFalse(appInstallMetadata.isStampPresent()); + assertFalse(appInstallMetadata.isStampVerified()); + assertFalse(appInstallMetadata.isStampTrusted()); + assertNull(appInstallMetadata.getStampCertificateHash()); // These are hardcoded in the test apk android manifest Map<String, String> allowedInstallers = appInstallMetadata.getAllowedInstallersAndCertificates(); diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java index b0def605db79..ccbaee41af7c 100644 --- a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java @@ -18,6 +18,11 @@ package com.android.server.lights; import static android.hardware.lights.LightsRequest.Builder; +import static android.graphics.Color.BLACK; +import static android.graphics.Color.BLUE; +import static android.graphics.Color.GREEN; +import static android.graphics.Color.WHITE; + import static com.google.common.truth.Truth.assertThat; import android.content.Context; @@ -92,7 +97,7 @@ public class LightsServiceTest { // When the session requests to turn 3/4 lights on: LightsManager.LightsSession session = manager.openSession(); - session.setLights(new Builder() + session.requestLights(new Builder() .setLight(manager.getLights().get(0), new LightState(0xf1)) .setLight(manager.getLights().get(1), new LightState(0xf2)) .setLight(manager.getLights().get(2), new LightState(0xf3)) @@ -114,18 +119,18 @@ public class LightsServiceTest { Light micLight = manager.getLights().get(0); // The light should begin by being off. - assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000); + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLACK); // When a session commits changes: LightsManager.LightsSession session = manager.openSession(); - session.setLights(new Builder().setLight(micLight, new LightState(0xff00ff00)).build()); + session.requestLights(new Builder().setLight(micLight, new LightState(GREEN)).build()); // Then the light should turn on. - assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff00ff00); + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(GREEN); // When the session goes away: session.close(); // Then the light should turn off. - assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000); + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLACK); } @Test @@ -138,15 +143,15 @@ public class LightsServiceTest { LightsManager.LightsSession session2 = manager.openSession(); // When session1 and session2 both request the same light: - session1.setLights(new Builder().setLight(micLight, new LightState(0xff0000ff)).build()); - session2.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build()); + session1.requestLights(new Builder().setLight(micLight, new LightState(BLUE)).build()); + session2.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build()); // Then session1 should win because it was created first. - assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff0000ff); + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLUE); // When session1 goes away: session1.close(); // Then session2 should have its request go into effect. - assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xffffffff); + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(WHITE); // When session2 goes away: session2.close(); @@ -162,10 +167,10 @@ public class LightsServiceTest { // When the session turns a light on: LightsManager.LightsSession session = manager.openSession(); - session.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build()); + session.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build()); // And then the session clears it again: - session.setLights(new Builder().clearLight(micLight).build()); + session.requestLights(new Builder().clearLight(micLight).build()); // Then the light should turn back off. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0); diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java index 4e63237a6091..e10cbbfe78ad 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java @@ -273,9 +273,8 @@ public final class ConversationStoreTest { // Ensure that futures were cancelled and the immediate flush occurred. assertEquals(0, mMockScheduledExecutorService.getFutures().size()); - // Expect to see 2 executes: loadConversationFromDisk and saveConversationsToDisk. - // loadConversationFromDisk gets called each time we call #resetConversationStore(). - assertEquals(2, mMockScheduledExecutorService.getExecutes().size()); + // Expect to see 1 execute: saveConversationsToDisk. + assertEquals(1, mMockScheduledExecutorService.getExecutes().size()); resetConversationStore(); ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID); diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index a4d63ac8a564..51996047a74b 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -183,6 +183,11 @@ public final class DataManagerTest { when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any( TimeUnit.class))).thenReturn(mScheduledFuture); + doAnswer(ans -> { + Runnable runnable = (Runnable) ans.getArguments()[0]; + runnable.run(); + return null; + }).when(mExecutorService).execute(any(Runnable.class)); when(mUserManager.getEnabledProfiles(USER_ID_PRIMARY)) .thenReturn(Arrays.asList( diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 9f3bf02384b0..6eec649f2ba5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManager.START_ABORTED; +import static android.app.ActivityManager.START_CANCELED; import static android.app.ActivityManager.START_CLASS_NOT_FOUND; import static android.app.ActivityManager.START_DELIVERED_TO_TOP; import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT; @@ -202,10 +203,11 @@ public class ActivityStarterTests extends ActivityTestsBase { final IApplicationThread caller = mock(IApplicationThread.class); final WindowProcessListener listener = mock(WindowProcessListener.class); + final ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = "com.android.test.package"; final WindowProcessController wpc = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP) - ? null : new WindowProcessController( - service, mock(ApplicationInfo.class), null, 0, -1, null, listener); + ? null : new WindowProcessController(service, ai, null, 0, -1, null, listener); doReturn(wpc).when(service).getProcessController(anyObject()); final Intent intent = new Intent(); @@ -344,6 +346,7 @@ public class ActivityStarterTests extends ActivityTestsBase { doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any()); doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyInt()); + doReturn(new ComponentName("", "")).when(mMockPackageManager).getSystemUiServiceComponent(); // Never review permissions doReturn(false).when(mMockPackageManager).isPermissionsReviewRequired(any(), anyInt()); @@ -655,6 +658,7 @@ public class ActivityStarterTests extends ActivityTestsBase { final WindowProcessListener listener = mock(WindowProcessListener.class); final ApplicationInfo ai = new ApplicationInfo(); ai.uid = callingUid; + ai.packageName = "com.android.test.package"; final WindowProcessController callerApp = new WindowProcessController(mService, ai, null, callingUid, -1, null, listener); callerApp.setHasForegroundActivities(hasForegroundActivities); @@ -892,7 +896,7 @@ public class ActivityStarterTests extends ActivityTestsBase { .execute(); // Simulate a failed start - starter.postStartActivityProcessing(null, START_ABORTED, null); + starter.postStartActivityProcessing(null, START_CANCELED, null); verify(recentTasks, times(1)).setFreezeTaskListReordering(); verify(recentTasks, times(1)).resetFreezeTaskListReorderingOnTimeout(); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 5cf1fbbacaf4..a380ece61935 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -69,7 +71,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR)); - assertNotNull(getController().getInsetsForDispatch(app).getSource(ITYPE_STATUS_BAR)); + assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR)); } @Test @@ -101,6 +103,34 @@ public class InsetsStateControllerTest extends WindowTestsBase { } @Test + public void testStripForDispatch_pip() { + final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); + final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + + getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); + getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null); + app.setWindowingMode(WINDOWING_MODE_PINNED); + + assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR)); + assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR)); + } + + @Test + public void testStripForDispatch_freeform() { + final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); + final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + + getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); + getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null); + app.setWindowingMode(WINDOWING_MODE_FREEFORM); + + assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR)); + assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR)); + } + + @Test public void testImeForDispatch() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index ac4c228fda9a..8f3ff52b8018 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -37,8 +37,8 @@ import android.graphics.Rect; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.server.LocalServices; import com.android.server.pm.PackageList; @@ -72,6 +72,10 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { ComponentName.createRelative("com.android.foo", ".BarActivity"); private static final ComponentName ALTERNATIVE_COMPONENT = ComponentName.createRelative("com.android.foo", ".AlternativeBarActivity"); + private static final String TEST_WINDOW_LAYOUT_AFFINITY = "135:.Affinity"; + private static final String TEST_ALTERNATIVE_WINDOW_LAYOUT_AFFINITY = + "246:.AlternativeAffinity"; + private static final String TEST_DIFFERENT_AFFINITY_WITH_SAME_UID = "135:.DifferentAffinity"; private static final int TEST_WINDOWING_MODE = WINDOWING_MODE_FREEFORM; private static final Rect TEST_BOUNDS = new Rect(100, 200, 300, 400); @@ -99,11 +103,12 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { public void setUp() throws Exception { mPersisterQueue = new TestPersisterQueue(); - final File cacheFolder = InstrumentationRegistry.getContext().getCacheDir(); + final File cacheFolder = + InstrumentationRegistry.getInstrumentation().getContext().getCacheDir(); mFolder = new File(cacheFolder, "launch_params_tests"); deleteRecursively(mFolder); - mDisplayUniqueId = "test:" + Integer.toString(sNextUniqueId++); + mDisplayUniqueId = "test:" + sNextUniqueId++; mTestDisplay = new TestDisplayContent.Builder(mService, 1000, 1500) .setUniqueId(mDisplayUniqueId).build(); when(mRootWindowContainer.getDisplayContent(eq(mDisplayUniqueId))) @@ -131,6 +136,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { LocalServices.addService(PackageManagerInternal.class, mMockPmi); when(mMockPmi.getPackageList(any())).thenReturn(new PackageList( Collections.singletonList(TEST_COMPONENT.getPackageName()), /* observer */ null)); + when(mMockPmi.getSystemUiServiceComponent()).thenReturn(new ComponentName("", "")); mTarget.onSystemReady(); final ArgumentCaptor<PackageManagerInternal.PackageListObserver> observerCaptor = @@ -210,6 +216,61 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { } @Test + public void testUsesRecordWithSameWindowLayoutAffinityInSameInstance_NoPreviousRecord() { + mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY; + mTarget.saveTask(mTestTask); + + mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY; + mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult); + + assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId); + assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode); + assertEquals(TEST_BOUNDS, mResult.mBounds); + } + + @Test + public void testUsesRecordWithSameWindowLayoutAffinityInSameInstance_HasOldPreviousRecord() + throws Exception { + mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY; + mTarget.saveTask(mTaskWithDifferentComponent); + + Thread.sleep(1); // Sleep 1ms so that the timestamp can for sure increase. + + mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY; + mTarget.saveTask(mTestTask); + + mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult); + + assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId); + assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode); + assertEquals(TEST_BOUNDS, mResult.mBounds); + } + + @Test + public void testReturnsEmptyLaunchParamsUidInLaunchAffinityMismatch() { + mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY; + mTarget.saveTask(mTestTask); + + mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_DIFFERENT_AFFINITY_WITH_SAME_UID; + mResult.mWindowingMode = WINDOWING_MODE_FULLSCREEN; + mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult); + + assertTrue("Result must be empty.", mResult.isEmpty()); + } + + @Test + public void testReturnsEmptyLaunchParamsWindowLayoutAffinityMismatch() { + mTestTask.affinity = TEST_WINDOW_LAYOUT_AFFINITY; + mTarget.saveTask(mTestTask); + + mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_ALTERNATIVE_WINDOW_LAYOUT_AFFINITY; + mResult.mWindowingMode = WINDOWING_MODE_FULLSCREEN; + mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult); + + assertTrue("Result must be empty.", mResult.isEmpty()); + } + + @Test public void testSavesAndRestoresLaunchParamsAcrossInstances() { mTarget.saveTask(mTestTask); mPersisterQueue.flush(); @@ -227,6 +288,52 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { } @Test + public void testUsesRecordWithSameWindowLayoutAffinityAcrossInstances_NoPreviousRecord() { + mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY; + mTarget.saveTask(mTestTask); + mPersisterQueue.flush(); + + final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor, + mUserFolderGetter); + target.onSystemReady(); + target.onUnlockUser(TEST_USER_ID); + + mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY; + target.getLaunchParams(mTaskWithDifferentComponent, null, mResult); + + assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId); + assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode); + assertEquals(TEST_BOUNDS, mResult.mBounds); + } + + @Test + public void testUsesRecordWithSameWindowLayoutAffinityAcrossInstances_HasOldPreviousRecord() + throws Exception { + mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY; + mTarget.saveTask(mTaskWithDifferentComponent); + mPersisterQueue.flush(); + + // Sleep 1s because many file systems only save last modified time as precise as 1s so we + // can for sure know the last modified time is different. + Thread.sleep(1000); + + mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY; + mTarget.saveTask(mTestTask); + mPersisterQueue.flush(); + + final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor, + mUserFolderGetter); + target.onSystemReady(); + target.onUnlockUser(TEST_USER_ID); + + target.getLaunchParams(mTaskWithDifferentComponent, null, mResult); + + assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId); + assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode); + assertEquals(TEST_BOUNDS, mResult.mBounds); + } + + @Test public void testClearsRecordsOfTheUserOnUserCleanUp() { mTarget.saveTask(mTestTask); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 55d12dbd0abb..560d03fe7d9e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -40,6 +40,7 @@ import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.IntentFilter; @@ -213,6 +214,9 @@ public class SystemServicesTestRule implements TestRule { anyString(), anyInt()); doReturn(null).when(packageManagerInternal).getDefaultHomeActivity(anyInt()); + ComponentName systemServiceComponent = new ComponentName("android.test.system.service", ""); + doReturn(systemServiceComponent).when(packageManagerInternal).getSystemUiServiceComponent(); + // PowerManagerInternal final PowerManagerInternal pmi = mock(PowerManagerInternal.class); final PowerSaveState state = new PowerSaveState.Builder().build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java index 53a3682a1efa..19ed7a9101de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -213,6 +213,16 @@ public class TaskOrganizerTests extends WindowTestsBase { } @Test + public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException { + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + stack.setWindowingMode(WINDOWING_MODE_PINNED); + + final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED); + verify(organizer, times(1)).taskAppeared(any()); + } + + @Test public void testTaskTransaction() { removeGlobalMinSizeRestriction(); final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot) @@ -241,6 +251,32 @@ public class TaskOrganizerTests extends WindowTestsBase { } @Test + public void testSetWindowingMode() { + final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot) + .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); + final WindowContainerTransaction t = new WindowContainerTransaction(); + + t.setWindowingMode(stack.mRemoteToken, WINDOWING_MODE_FULLSCREEN); + mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + + assertEquals(WINDOWING_MODE_FULLSCREEN, stack.getWindowingMode()); + } + + @Test + public void testSetActivityWindowingMode() { + final ActivityRecord record = makePipableActivity(); + final ActivityStack stack = record.getStack(); + final WindowContainerTransaction t = new WindowContainerTransaction(); + + t.setWindowingMode(stack.mRemoteToken, WINDOWING_MODE_PINNED); + t.setActivityWindowingMode(stack.mRemoteToken, WINDOWING_MODE_FULLSCREEN); + mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + + assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode()); + assertEquals(WINDOWING_MODE_PINNED, stack.getWindowingMode()); + } + + @Test public void testContainerChanges() { removeGlobalMinSizeRestriction(); final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot) diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java index e1475a453330..91c3c2782d94 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java @@ -77,7 +77,8 @@ public class TestIWindow extends IWindow.Stub { } @Override - public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync) + public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom, + boolean sync) throws RemoteException { } @@ -85,7 +86,6 @@ public class TestIWindow extends IWindow.Stub { public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras, boolean sync) throws RemoteException { } - @Override public void dispatchDragEvent(DragEvent event) throws RemoteException { } diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index aa665241c50b..900f014a0218 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -17,19 +17,29 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.DisplayInfo; import android.view.Gravity; @@ -139,4 +149,124 @@ public class WallpaperControllerTests extends WindowTestsBase { assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getConfiguration().orientation); assertEquals(portraitFrame, wallpaperWindow.getFrameLw()); } + + @Test + public void testWallpaperZoom() throws RemoteException { + final DisplayContent dc = mWm.mRoot.getDefaultDisplay(); + final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, + mock(IBinder.class), true, dc, true /* ownerCanManageAppTokens */); + final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, + "wallpaperWindow"); + wallpaperWindow.getAttrs().privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; + + final WindowState homeWindow = createWallpaperTargetWindow(dc); + + spyOn(dc.mWallpaperController); + doReturn(true).when(dc.mWallpaperController).isWallpaperVisible(); + + dc.mWallpaperController.adjustWallpaperWindows(); + + spyOn(wallpaperWindow.mClient); + + float zoom = .5f; + dc.mWallpaperController.setWallpaperZoomOut(homeWindow, zoom); + assertEquals(zoom, wallpaperWindow.mWallpaperZoomOut, .01f); + verify(wallpaperWindow.mClient).dispatchWallpaperOffsets(anyFloat(), anyFloat(), anyFloat(), + anyFloat(), eq(zoom), anyBoolean()); + } + + @Test + public void testWallpaperZoom_shouldNotScaleWallpaper() throws RemoteException { + final DisplayContent dc = mWm.mRoot.getDefaultDisplay(); + final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, + mock(IBinder.class), true, dc, true /* ownerCanManageAppTokens */); + final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, + "wallpaperWindow"); + wallpaperWindow.getAttrs().privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; + + final WindowState homeWindow = createWallpaperTargetWindow(dc); + + spyOn(dc.mWallpaperController); + doReturn(true).when(dc.mWallpaperController).isWallpaperVisible(); + + dc.mWallpaperController.adjustWallpaperWindows(); + + spyOn(wallpaperWindow.mClient); + + float newZoom = .5f; + wallpaperWindow.mShouldScaleWallpaper = false; + // Set zoom, and make sure the window animator scale didn't actually change, but the zoom + // value did, and we do dispatch the zoom to the wallpaper service + dc.mWallpaperController.setWallpaperZoomOut(homeWindow, newZoom); + assertEquals(newZoom, wallpaperWindow.mWallpaperZoomOut, .01f); + assertEquals(1f, wallpaperWindow.mWinAnimator.mWallpaperScale, .01f); + verify(wallpaperWindow.mClient).dispatchWallpaperOffsets(anyFloat(), anyFloat(), anyFloat(), + anyFloat(), eq(newZoom), anyBoolean()); + } + + @Test + public void testWallpaperZoom_multipleCallers() { + final DisplayContent dc = mWm.mRoot.getDefaultDisplay(); + final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, + mock(IBinder.class), true, dc, + true /* ownerCanManageAppTokens */); + final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, + "wallpaperWindow"); + wallpaperWindow.getAttrs().privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; + + + spyOn(dc.mWallpaperController); + doReturn(true).when(dc.mWallpaperController).isWallpaperVisible(); + + final WindowState homeWindow = createWallpaperTargetWindow(dc); + + WindowState otherWindow = createWindow(null /* parent */, TYPE_APPLICATION, dc, + "otherWindow"); + + dc.mWallpaperController.adjustWallpaperWindows(); + + spyOn(wallpaperWindow.mClient); + + // Set zoom from 2 windows + float homeWindowInitialZoom = .5f; + float otherWindowInitialZoom = .7f; + dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowInitialZoom); + dc.mWallpaperController.setWallpaperZoomOut(otherWindow, otherWindowInitialZoom); + // Make sure the largest one wins + assertEquals(otherWindowInitialZoom, wallpaperWindow.mWallpaperZoomOut, .01f); + + // Change zoom to a larger zoom from homeWindow + float homeWindowZoom2 = .8f; + dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowZoom2); + // New zoom should be current + assertEquals(homeWindowZoom2, wallpaperWindow.mWallpaperZoomOut, .01f); + + // Set homeWindow zoom to a lower zoom, but keep the one from otherWindow + dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowInitialZoom); + + // Zoom from otherWindow should be the current. + assertEquals(otherWindowInitialZoom, wallpaperWindow.mWallpaperZoomOut, .01f); + } + + + private WindowState createWallpaperTargetWindow(DisplayContent dc) { + final ActivityRecord homeActivity = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService) + .setStack(dc.getRootHomeTask()) + .setCreateTask(true) + .build(); + homeActivity.setVisibility(true); + + WindowState appWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION, + homeActivity, "wallpaperTargetWindow"); + appWindow.getAttrs().flags |= FLAG_SHOW_WALLPAPER; + appWindow.mHasSurface = true; + spyOn(appWindow); + doReturn(true).when(appWindow).isDrawFinishedLw(); + + homeActivity.addWindow(appWindow); + return appWindow; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 34e487bc1e94..07a6179c00bd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.app.IApplicationThread; +import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.platform.test.annotations.Presubmit; @@ -55,8 +56,11 @@ public class WindowProcessControllerTests extends ActivityTestsBase { @Before public void setUp() { mMockListener = mock(WindowProcessListener.class); + + ApplicationInfo info = mock(ApplicationInfo.class); + info.packageName = "test.package.name"; mWpc = new WindowProcessController( - mService, mock(ApplicationInfo.class), null, 0, -1, null, mMockListener); + mService, info, null, 0, -1, null, mMockListener); mWpc.setThread(mock(IApplicationThread.class)); } @@ -176,6 +180,26 @@ public class WindowProcessControllerTests extends ActivityTestsBase { assertEquals(mWpc.getLastReportedConfiguration(), newConfig); } + @Test + public void testActivityNotOverridingSystemUiProcessConfig() { + final ComponentName systemUiServiceComponent = mService.getSysUiServiceComponentLocked(); + ApplicationInfo applicationInfo = mock(ApplicationInfo.class); + applicationInfo.packageName = systemUiServiceComponent.getPackageName(); + + WindowProcessController wpc = new WindowProcessController( + mService, applicationInfo, null, 0, -1, null, mMockListener); + wpc.setThread(mock(IApplicationThread.class)); + + final ActivityRecord activity = new ActivityBuilder(mService) + .setCreateTask(true) + .setUseProcess(wpc) + .build(); + + wpc.addActivityIfNeeded(activity); + // System UI owned processes should not be registered for activity config changes. + assertFalse(wpc.registeredForActivityConfigChanges()); + } + private TestDisplayContent createTestDisplayContentInContainer() { return new TestDisplayContent.Builder(mService, 1000, 1500).build(); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java index af81ab6339f3..be0987d745ba 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java @@ -257,6 +257,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { int userHandle) { SQLiteDatabase db = getReadableDatabase(); Cursor c = db.rawQuery(selectQuery, null); + if (DBG) { + Slog.w(TAG, "querying database: " + selectQuery); + } try { if (c.moveToFirst()) { @@ -334,7 +337,10 @@ public class DatabaseHelper extends SQLiteOpenHelper { return model; } while (c.moveToNext()); } - Slog.w(TAG, "No SoundModel available for the given keyphrase"); + + if (DBG) { + Slog.w(TAG, "No SoundModel available for the given keyphrase"); + } } finally { c.close(); db.close(); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 0b24dd2fe15a..0eba07b118b5 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -41,6 +41,7 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.soundtrigger.IRecognitionStatusCallback; +import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.hardware.soundtrigger.KeyphraseMetadata; import android.hardware.soundtrigger.ModelParams; import android.hardware.soundtrigger.SoundTrigger; @@ -223,6 +224,7 @@ public class VoiceInteractionManagerService extends SystemService { class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub { VoiceInteractionManagerServiceImpl mImpl; + KeyphraseEnrollmentInfo mEnrollmentApplicationInfo; private boolean mSafeMode; private int mCurUser; @@ -447,6 +449,15 @@ public class VoiceInteractionManagerService extends SystemService { } } + private void getOrCreateEnrollmentApplicationInfo() { + synchronized (this) { + if (mEnrollmentApplicationInfo == null) { + mEnrollmentApplicationInfo = new KeyphraseEnrollmentInfo( + mContext.getPackageManager()); + } + } + } + private void setCurrentUserLocked(@UserIdInt int userHandle) { mCurUser = userHandle; final UserInfo userInfo = mUserManagerInternal.getUserInfo(mCurUser); @@ -1380,12 +1391,17 @@ public class VoiceInteractionManagerService extends SystemService { pw.println(" mCurUserUnlocked: " + mCurUserUnlocked); pw.println(" mCurUserSupported: " + mCurUserSupported); dumpSupportedUsers(pw, " "); + if (mEnrollmentApplicationInfo == null) { + pw.println(" (No enrollment application info)"); + } else { + pw.println(" " + mEnrollmentApplicationInfo.toString()); + } mDbHelper.dump(pw); if (mImpl == null) { pw.println(" (No active implementation)"); - return; + } else { + mImpl.dumpLocked(fd, pw, args); } - mImpl.dumpLocked(fd, pw, args); } mSoundTriggerInternal.dump(fd, pw, args); } @@ -1438,8 +1454,9 @@ public class VoiceInteractionManagerService extends SystemService { } private boolean isCallerTrustedEnrollmentApplication() { - return mImpl.mEnrollmentApplicationInfo.isUidSupportedEnrollmentApplication( - Binder.getCallingUid()); + getOrCreateEnrollmentApplicationInfo(); + return mEnrollmentApplicationInfo.isUidSupportedEnrollmentApplication( + Binder.getCallingUid()); } private void setImplLocked(VoiceInteractionManagerServiceImpl impl) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index b813f87f335f..a62b03ca82e4 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -36,7 +36,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; -import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -79,7 +78,6 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne final IActivityManager mAm; final IActivityTaskManager mAtm; final VoiceInteractionServiceInfo mInfo; - final KeyphraseEnrollmentInfo mEnrollmentApplicationInfo; final ComponentName mSessionComponentName; final IWindowManager mIWindowManager; boolean mBound = false; @@ -135,7 +133,6 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne mComponent = service; mAm = ActivityManager.getService(); mAtm = ActivityTaskManager.getService(); - mEnrollmentApplicationInfo = new KeyphraseEnrollmentInfo(context.getPackageManager()); VoiceInteractionServiceInfo info; try { info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser); @@ -406,7 +403,6 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne pw.println(" Active session:"); mActiveSession.dump(" ", pw); } - pw.println(" " + mEnrollmentApplicationInfo.toString()); } void startLocked() { diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java index 36c637723c0a..c832f53ae073 100644 --- a/telecomm/java/android/telecom/CallRedirectionService.java +++ b/telecomm/java/android/telecom/CallRedirectionService.java @@ -38,16 +38,14 @@ import com.android.internal.telecom.ICallRedirectionService; * * <p> * Below is an example manifest registration for a {@code CallRedirectionService}. - * <pre> * {@code * <service android:name="your.package.YourCallRedirectionServiceImplementation" - * android:permission="android.permission.BIND_REDIRECTION_SERVICE"> + * android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE"> * <intent-filter> * <action android:name="android.telecom.CallRedirectionService"/> * </intent-filter> * </service> * } - * </pre> */ public abstract class CallRedirectionService extends Service { /** diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java index 0093843c4a27..bebbbd01fd88 100644 --- a/telecomm/java/android/telecom/DisconnectCause.java +++ b/telecomm/java/android/telecom/DisconnectCause.java @@ -80,17 +80,20 @@ public final class DisconnectCause implements Parcelable { * Reason code (returned via {@link #getReason()}) which indicates that a call could not be * completed because the cellular radio is off or out of service, the device is connected to * a wifi network, but the user has not enabled wifi calling. + * @hide */ public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF"; /** * Reason code (returned via {@link #getReason()}), which indicates that the video telephony * call was disconnected because IMS access is blocked. + * @hide */ public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED"; /** * Reason code, which indicates that the conference call is simulating single party conference. + * @hide */ public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL"; diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index 4e6e1a53113e..768c8eebf067 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -53,7 +53,6 @@ public final class PhoneAccount implements Parcelable { * {@link android.telecom.ConnectionService}. * @hide */ - @SystemApi public static final String EXTRA_SORT_ORDER = "android.telecom.extra.SORT_ORDER"; @@ -89,7 +88,6 @@ public final class PhoneAccount implements Parcelable { * rather than cellular calls. * @hide */ - @SystemApi public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE"; @@ -114,7 +112,6 @@ public final class PhoneAccount implements Parcelable { * * @hide */ - @SystemApi public static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK = "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK"; @@ -163,7 +160,6 @@ public final class PhoneAccount implements Parcelable { * in progress. * @hide */ - @SystemApi public static final String EXTRA_PLAY_CALL_RECORDING_TONE = "android.telecom.extra.PLAY_CALL_RECORDING_TONE"; @@ -258,7 +254,6 @@ public final class PhoneAccount implements Parcelable { * See {@link #getCapabilities} * @hide */ - @SystemApi public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 0x80; /** @@ -282,7 +277,6 @@ public final class PhoneAccount implements Parcelable { * convert all outgoing video calls to emergency numbers to audio-only. * @hide */ - @SystemApi public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200; /** @@ -340,7 +334,6 @@ public final class PhoneAccount implements Parcelable { * * @hide */ - @SystemApi public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000; /** diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 7f4fcc0ea63c..17922560c93a 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -318,13 +318,13 @@ public class TelecomManager { * the remote handle of the new call. * @hide */ - @SystemApi public static final String EXTRA_UNKNOWN_CALL_HANDLE = "android.telecom.extra.UNKNOWN_CALL_HANDLE"; /** * Optional extra for incoming and outgoing calls containing a long which specifies the time the * call was created. This value is in milliseconds since boot. + * @hide */ public static final String EXTRA_CALL_CREATED_TIME_MILLIS = "android.telecom.extra.CALL_CREATED_TIME_MILLIS"; @@ -388,7 +388,6 @@ public class TelecomManager { * </ul> * @hide */ - @SystemApi public static final String EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE"; @@ -731,7 +730,6 @@ public class TelecomManager { * @see #EXTRA_CURRENT_TTY_MODE * @hide */ - @SystemApi public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED"; @@ -746,7 +744,6 @@ public class TelecomManager { * </ul> * @hide */ - @SystemApi public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE"; @@ -757,7 +754,6 @@ public class TelecomManager { * @see #EXTRA_TTY_PREFERRED_MODE * @hide */ - @SystemApi public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED"; @@ -768,7 +764,6 @@ public class TelecomManager { * * @hide */ - @SystemApi public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE"; @@ -846,7 +841,6 @@ public class TelecomManager { * * @hide */ - @SystemApi public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE"; /** @@ -943,8 +937,8 @@ public class TelecomManager { */ public TelecomManager(Context context, ITelecomService telecomServiceImpl) { Context appContext = context.getApplicationContext(); - if (appContext != null && Objects.equals(context.getFeatureId(), - appContext.getFeatureId())) { + if (appContext != null && Objects.equals(context.getAttributionTag(), + appContext.getAttributionTag())) { mContext = appContext; } else { mContext = context; @@ -978,7 +972,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().getDefaultOutgoingPhoneAccount(uriScheme, - mContext.getOpPackageName(), mContext.getFeatureId()); + mContext.getOpPackageName(), mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getDefaultOutgoingPhoneAccount", e); @@ -1176,7 +1170,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().getSelfManagedPhoneAccounts(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getSelfManagedPhoneAccounts()", e); @@ -1202,7 +1196,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().getCallCapablePhoneAccounts(includeDisabledAccounts, - mContext.getOpPackageName(), mContext.getFeatureId()); + mContext.getOpPackageName(), mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts(" + @@ -1506,7 +1500,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().isVoiceMailNumber(accountHandle, number, - mContext.getOpPackageName(), mContext.getFeatureId()); + mContext.getOpPackageName(), mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling ITelecomService#isVoiceMailNumber.", e); @@ -1528,7 +1522,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().getVoiceMailNumber(accountHandle, - mContext.getOpPackageName(), mContext.getFeatureId()); + mContext.getOpPackageName(), mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e); @@ -1549,7 +1543,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().getLine1Number(accountHandle, - mContext.getOpPackageName(), mContext.getFeatureId()); + mContext.getOpPackageName(), mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling ITelecomService#getLine1Number.", e); @@ -1571,7 +1565,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().isInCall(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling isInCall().", e); @@ -1597,7 +1591,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().isInManagedCall(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling isInManagedCall().", e); @@ -1778,7 +1772,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().isTtySupported(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException attempting to get TTY supported state.", e); @@ -1803,7 +1797,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().getCurrentTtyMode(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException attempting to get the current TTY mode.", e); @@ -2033,7 +2027,7 @@ public class TelecomManager { if (service != null) { try { service.showInCallScreen(showDialpad, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#showCallScreen", e); } @@ -2096,7 +2090,7 @@ public class TelecomManager { } try { service.placeCall(address, extras == null ? new Bundle() : extras, - mContext.getOpPackageName(), mContext.getFeatureId()); + mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#placeCall", e); } diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index 9ae86c8c586b..dcd4eb519b74 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -1,7 +1,6 @@ package android.telephony; import android.annotation.IntDef; -import android.provider.Telephony; import android.telecom.Connection; import android.telephony.data.ApnSetting; @@ -653,15 +652,6 @@ public class Annotation { @Retention(RetentionPolicy.SOURCE) public @interface UiccAppType{} - /** @hide */ - @IntDef({ - Telephony.Carriers.SKIP_464XLAT_DEFAULT, - Telephony.Carriers.SKIP_464XLAT_DISABLE, - Telephony.Carriers.SKIP_464XLAT_ENABLE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Skip464XlatStatus {} - /** * Override network type */ diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 441550804bbe..e2112a59beab 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2382,17 +2382,16 @@ public class CarrierConfigManager { * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and * not be used for calculating signal level. If multiple measures are set bit, the parameter * whose value is smallest is used to indicate the signal level. + * <UL> + * <LI>RSRP = 1 << 0</LI> + * <LI>RSRQ = 1 << 1</LI> + * <LI>RSSNR = 1 << 2</LI> + * </UL> + * <p> The value of this key must be bitwise OR of {@link CellSignalStrengthLte#USE_RSRP}, + * {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}. * - * RSRP = 1 << 0, - * RSRQ = 1 << 1, - * RSSNR = 1 << 2, - * - * The value of this key must be bitwise OR of {@link CellSignalStrengthLte#USE_RSRP}, - * {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}. - * - * For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1). - * If the key is invalid or not configured, a default value (RSRP = 1 << 0) - * will apply. + * <p> For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1). + * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply. * * @hide */ @@ -2401,16 +2400,18 @@ public class CarrierConfigManager { /** * List of 4 customized 5G SS reference signal received power (SSRSRP) thresholds. - * + * <p> * Reference: 3GPP TS 38.215 - * + * <p> * 4 threshold integers must be within the boundaries [-140 dB, -44 dB], and the levels are: - * "NONE: [-140, threshold1]" - * "POOR: (threshold1, threshold2]" - * "MODERATE: (threshold2, threshold3]" - * "GOOD: (threshold3, threshold4]" - * "EXCELLENT: (threshold4, -44]" - * + * <UL> + * <LI>"NONE: [-140, threshold1]"</LI> + * <LI>"POOR: (threshold1, threshold2]"</LI> + * <LI>"MODERATE: (threshold2, threshold3]"</LI> + * <LI>"GOOD: (threshold3, threshold4]"</LI> + * <LI>"EXCELLENT: (threshold4, -44]"</LI> + * </UL> + * <p> * This key is considered invalid if the format is violated. If the key is invalid or * not configured, a default value set will apply. */ @@ -2419,16 +2420,18 @@ public class CarrierConfigManager { /** * List of 4 customized 5G SS reference signal received quality (SSRSRQ) thresholds. - * + * <p> * Reference: 3GPP TS 38.215 - * + * <p> * 4 threshold integers must be within the boundaries [-20 dB, -3 dB], and the levels are: - * "NONE: [-20, threshold1]" - * "POOR: (threshold1, threshold2]" - * "MODERATE: (threshold2, threshold3]" - * "GOOD: (threshold3, threshold4]" - * "EXCELLENT: (threshold4, -3]" - * + * <UL> + * <LI>"NONE: [-20, threshold1]"</LI> + * <LI>"POOR: (threshold1, threshold2]"</LI> + * <LI>"MODERATE: (threshold2, threshold3]"</LI> + * <LI>"GOOD: (threshold3, threshold4]"</LI> + * <LI>"EXCELLENT: (threshold4, -3]"</LI> + * </UL> + * <p> * This key is considered invalid if the format is violated. If the key is invalid or * not configured, a default value set will apply. */ @@ -2437,17 +2440,19 @@ public class CarrierConfigManager { /** * List of 4 customized 5G SS signal-to-noise and interference ratio (SSSINR) thresholds. - * + * <p> * Reference: 3GPP TS 38.215, * 3GPP TS 38.133 10.1.16.1 - * + * <p> * 4 threshold integers must be within the boundaries [-23 dB, 40 dB], and the levels are: - * "NONE: [-23, threshold1]" - * "POOR: (threshold1, threshold2]" - * "MODERATE: (threshold2, threshold3]" - * "GOOD: (threshold3, threshold4]" - * "EXCELLENT: (threshold4, 40]" - * + * <UL> + * <LI>"NONE: [-23, threshold1]"</LI> + * <LI>"POOR: (threshold1, threshold2]"</LI> + * <LI>"MODERATE: (threshold2, threshold3]"</LI> + * <LI>"GOOD: (threshold3, threshold4]"</LI> + * <LI>"EXCELLENT: (threshold4, 40]"</LI> + * </UL> + * <p> * This key is considered invalid if the format is violated. If the key is invalid or * not configured, a default value set will apply. */ @@ -2462,19 +2467,19 @@ public class CarrierConfigManager { * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and * not be used for calculating signal level. If multiple measures are set bit, the parameter * whose value is smallest is used to indicate the signal level. - * - * SSRSRP = 1 << 0, - * SSRSRQ = 1 << 1, - * SSSINR = 1 << 2, - * + * <UL> + * <LI>SSRSRP = 1 << 0</LI> + * <LI>SSRSRQ = 1 << 1</LI> + * <LI>SSSINR = 1 << 2</LI> + * </UL> * The value of this key must be bitwise OR of {@link CellSignalStrengthNr#USE_SSRSRP}, * {@link CellSignalStrengthNr#USE_SSRSRQ}, {@link CellSignalStrengthNr#USE_SSSINR}. * - * For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2). + * <p> For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2). * If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply. * - * Reference: 3GPP TS 38.215, - * 3GPP TS 38.133 10.1.16.1 + * <p> Reference: 3GPP TS 38.215, + * 3GPP TS 38.133 10.1.16.1 * * @hide */ @@ -3530,6 +3535,15 @@ public class CarrierConfigManager { "support_wps_over_ims_bool"; /** + * The two digital number pattern of MMI code which is defined by carrier. + * If the dial number matches this pattern, it will be dialed out normally not USSD. + * + * @hide + */ + public static final String KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY = + "mmi_two_digit_number_pattern_string_array"; + + /** * Holds the list of carrier certificate hashes. * Note that each carrier has its own certificates. */ @@ -4086,6 +4100,7 @@ public class CarrierConfigManager { new int[] {4 /* BUSY */}); sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false); sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000); + sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]); sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT, CellSignalStrengthLte.USE_RSRP); // Default wifi configurations. @@ -4147,7 +4162,7 @@ public class CarrierConfigManager { return null; } return loader.getConfigForSubIdWithFeature(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { Rlog.e(TAG, "Error getting config for subId " + subId + ": " + ex.toString()); diff --git a/telephony/java/android/telephony/PinResult.java b/telephony/java/android/telephony/PinResult.java index c14bd91d022c..98d6448e77ea 100644 --- a/telephony/java/android/telephony/PinResult.java +++ b/telephony/java/android/telephony/PinResult.java @@ -19,7 +19,6 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,7 +31,6 @@ import java.util.Objects; * * @hide */ -@SystemApi public final class PinResult implements Parcelable { /** @hide */ @IntDef({ diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 45cba518f933..dd20d065fc83 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -580,7 +580,6 @@ public class ServiceState implements Parcelable { * * @hide */ - @SystemApi public @RegState int getDataRegistrationState() { return getDataRegState(); } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 8ac9023b33dc..01a40f55e29f 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1144,7 +1144,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1178,7 +1178,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1212,7 +1212,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex, - mContext.getOpPackageName(), mContext.getFeatureId()); + mContext.getOpPackageName(), mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1236,7 +1236,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAllSubInfoList(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1300,8 +1300,13 @@ public class SubscriptionManager { * both active and hidden SubscriptionInfos. * */ - public @Nullable List<SubscriptionInfo> getActiveAndHiddenSubscriptionInfoList() { - return getActiveSubscriptionInfoList(/* userVisibleonly */false); + public @NonNull List<SubscriptionInfo> getCompleteActiveSubscriptionInfoList() { + List<SubscriptionInfo> completeList = getActiveSubscriptionInfoList( + /* userVisibleonly */false); + if (completeList == null) { + completeList = new ArrayList<>(); + } + return completeList; } /** @@ -1317,7 +1322,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1368,7 +1373,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1486,7 +1491,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAllSubInfoCount(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1515,7 +1520,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -2270,7 +2275,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { resultValue = iSub.getSubscriptionProperty(subId, propKey, - context.getOpPackageName(), context.getFeatureId()); + context.getOpPackageName(), context.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -2434,7 +2439,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { return iSub.isActiveSubId(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { } @@ -2697,13 +2702,14 @@ public class SubscriptionManager { @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public @NonNull List<SubscriptionInfo> getOpportunisticSubscriptions() { String contextPkg = mContext != null ? mContext.getOpPackageName() : "<unknown>"; - String contextFeature = mContext != null ? mContext.getFeatureId() : null; + String contextAttributionTag = mContext != null ? mContext.getAttributionTag() : null; List<SubscriptionInfo> subInfoList = null; try { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { - subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature); + subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, + contextAttributionTag); } } catch (RemoteException ex) { // ignore it @@ -2942,7 +2948,7 @@ public class SubscriptionManager { public @NonNull List<SubscriptionInfo> getSubscriptionsInGroup(@NonNull ParcelUuid groupUuid) { Preconditions.checkNotNull(groupUuid, "groupUuid can't be null"); String contextPkg = mContext != null ? mContext.getOpPackageName() : "<unknown>"; - String contextFeature = mContext != null ? mContext.getFeatureId() : null; + String contextAttributionTag = mContext != null ? mContext.getAttributionTag() : null; if (VDBG) { logd("[getSubscriptionsInGroup]+ groupUuid:" + groupUuid); } @@ -2951,7 +2957,8 @@ public class SubscriptionManager { try { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { - result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature); + result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, + contextAttributionTag); } else { if (!isSystemProcess()) { throw new IllegalStateException("telephony service is null."); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 23a2b9cae125..610ec5eee897 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -345,10 +345,10 @@ public class TelephonyManager { mSubId = subId; Context appContext = context.getApplicationContext(); if (appContext != null) { - if (Objects.equals(context.getFeatureId(), appContext.getFeatureId())) { + if (Objects.equals(context.getAttributionTag(), appContext.getAttributionTag())) { mContext = appContext; } else { - mContext = appContext.createFeatureContext(context.getFeatureId()); + mContext = appContext.createAttributionContext(context.getAttributionTag()); } } else { mContext = context; @@ -393,12 +393,12 @@ public class TelephonyManager { } } - private String getFeatureId() { + private String getAttributionTag() { // For legacy reasons the TelephonyManager has API for getting // a static instance with no context set preventing us from - // getting the feature Id. + // getting the attribution tag. if (mContext != null) { - return mContext.getFeatureId(); + return mContext.getAttributionTag(); } return null; } @@ -1896,7 +1896,7 @@ public class TelephonyManager { try { return telephony.getDeviceSoftwareVersionForSlot(slotIndex, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -1938,7 +1938,7 @@ public class TelephonyManager { if (telephony == null) return null; return telephony.getDeviceIdWithFeature(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -1983,7 +1983,7 @@ public class TelephonyManager { if (info == null) return null; return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -2041,7 +2041,7 @@ public class TelephonyManager { if (telephony == null) return null; try { - return telephony.getImeiForSlot(slotIndex, getOpPackageName(), getFeatureId()); + return telephony.getImeiForSlot(slotIndex, getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -2135,7 +2135,8 @@ public class TelephonyManager { if (telephony == null) return null; try { - String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName(), getFeatureId()); + String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName(), + getAttributionTag()); if (TextUtils.isEmpty(meid)) { Log.d(TAG, "getMeid: return null because MEID is not available"); return null; @@ -2237,7 +2238,7 @@ public class TelephonyManager { if (info == null) return null; String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); if (Log.isLoggable(TAG, Log.VERBOSE)) { Rlog.v(TAG, "Nai = " + nai); } @@ -2271,7 +2272,7 @@ public class TelephonyManager { } CellIdentity cellIdentity = telephony.getCellLocation(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); CellLocation cl = cellIdentity.asCellLocation(); if (cl == null || cl.isEmpty()) { Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty or" @@ -2355,7 +2356,7 @@ public class TelephonyManager { if (telephony == null) return null; return telephony.getNeighboringCellInfo(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -2371,7 +2372,12 @@ public class TelephonyManager { public static final int PHONE_TYPE_CDMA = PhoneConstants.PHONE_TYPE_CDMA; /** Phone is via SIP. */ public static final int PHONE_TYPE_SIP = PhoneConstants.PHONE_TYPE_SIP; - /** Phone is via IMS. */ + + /** + * Phone is via IMS. + * + * @hide + */ public static final int PHONE_TYPE_IMS = PhoneConstants.PHONE_TYPE_IMS; /** @@ -2379,7 +2385,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi public static final int PHONE_TYPE_THIRD_PARTY = PhoneConstants.PHONE_TYPE_THIRD_PARTY; /** @@ -2957,7 +2962,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } else { // This can happen when the ITelephony interface is not up yet. return NETWORK_TYPE_UNKNOWN; @@ -3022,7 +3027,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } else { // This can happen when the ITelephony interface is not up yet. return NETWORK_TYPE_UNKNOWN; @@ -3059,7 +3064,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getVoiceNetworkTypeForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } else { // This can happen when the ITelephony interface is not up yet. return NETWORK_TYPE_UNKNOWN; @@ -3763,29 +3768,6 @@ public class TelephonyManager { } /** - * Returns the ISO-3166 country code equivalent for the SIM provider's country code - * of the default subscription - * <p> - * The ISO-3166 country code is provided in lowercase 2 character format. - * @return the lowercase 2 character ISO-3166 country code, or empty string is not available. - * <p> - * Note: This API is introduced to unblock mainlining work as the following APIs in - * Linkify.java invokes getSimCountryIso() without a context. TODO(Bug 144576376): remove - * this API once the following APIs are redesigned to access telephonymanager with a context. - * - * {@link Linkify#addLinks(@NonNull Spannable text, @LinkifyMask int mask)} - * {@link Linkify#addLinks(@NonNull Spannable text, @LinkifyMask int mask, - @Nullable Function<String, URLSpan> urlSpanFactory)} - * - * @hide - */ - @SystemApi - @NonNull - public static String getDefaultSimCountryIso() { - return getSimCountryIso(SubscriptionManager.getDefaultSubscriptionId()); - } - - /** * Returns the ISO country code equivalent for the SIM provider's country code. * * @param subId for which SimCountryIso is returned @@ -3868,7 +3850,7 @@ public class TelephonyManager { if (info == null) return null; return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -3912,7 +3894,7 @@ public class TelephonyManager { if (telephony == null) return PhoneConstants.LTE_ON_CDMA_UNKNOWN; return telephony.getLteOnCdmaModeForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { // Assume no ICC card if remote exception which shouldn't happen return PhoneConstants.LTE_ON_CDMA_UNKNOWN; @@ -4141,7 +4123,7 @@ public class TelephonyManager { if (info == null) return null; return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4309,7 +4291,7 @@ public class TelephonyManager { if (info == null) return null; return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4333,7 +4315,7 @@ public class TelephonyManager { if (info == null) return null; return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4384,7 +4366,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { } catch (NullPointerException ex) { } @@ -4396,7 +4378,7 @@ public class TelephonyManager { if (info == null) return null; return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4475,7 +4457,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) alphaTag = telephony.getLine1AlphaTagForDisplay(subId, - getOpPackageName(), getFeatureId()); + getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { } catch (NullPointerException ex) { } @@ -4487,7 +4469,7 @@ public class TelephonyManager { if (info == null) return null; return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4517,7 +4499,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) return telephony.getMergedSubscriberIds(getSubId(), getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { } catch (NullPointerException ex) { } @@ -4574,7 +4556,7 @@ public class TelephonyManager { IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; - return info.getMsisdnForSubscriber(subId, getOpPackageName(), getFeatureId()); + return info.getMsisdnForSubscriber(subId, getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4609,7 +4591,7 @@ public class TelephonyManager { if (info == null) return null; return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4734,7 +4716,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getVisualVoicemailPackageName(mContext.getOpPackageName(), - getFeatureId(), getSubId()); + getAttributionTag(), getSubId()); } } catch (RemoteException ex) { } catch (NullPointerException ex) { @@ -5171,7 +5153,7 @@ public class TelephonyManager { if (telephony == null) return 0; return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return 0; } catch (NullPointerException ex) { @@ -5208,7 +5190,7 @@ public class TelephonyManager { if (info == null) return null; return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -5591,7 +5573,7 @@ public class TelephonyManager { (TelephonyRegistryManager) mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); if (telephonyRegistry != null) { - telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getFeatureId(), + telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getAttributionTag(), listener, events, notifyNow); } else { Rlog.w(TAG, "telephony registry not ready."); @@ -5624,7 +5606,7 @@ public class TelephonyManager { if (telephony == null) return -1; return telephony.getCdmaEriIconIndexForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { // the phone process is restarting. return -1; @@ -5648,7 +5630,7 @@ public class TelephonyManager { if (telephony == null) return -1; return telephony.getCdmaEriIconModeForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { // the phone process is restarting. return -1; @@ -5680,7 +5662,7 @@ public class TelephonyManager { if (telephony == null) return null; return telephony.getCdmaEriTextForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { // the phone process is restarting. return null; @@ -5772,7 +5754,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony == null) return null; - return telephony.getAllCellInfo(getOpPackageName(), getFeatureId()); + return telephony.getAllCellInfo(getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { } catch (NullPointerException ex) { } @@ -5872,7 +5854,7 @@ public class TelephonyManager { Binder.restoreCallingIdentity(identity); } } - }, getOpPackageName(), getFeatureId()); + }, getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { } } @@ -5923,7 +5905,7 @@ public class TelephonyManager { Binder.restoreCallingIdentity(identity); } } - }, getOpPackageName(), getFeatureId(), workSource); + }, getOpPackageName(), getAttributionTag(), workSource); } catch (RemoteException ex) { } } @@ -7209,7 +7191,7 @@ public class TelephonyManager { if (telephony == null) return null; return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -7243,7 +7225,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony == null) return -1; return telephony.setForbiddenPlmns( - getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getFeatureId()); + getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { Rlog.e(TAG, "setForbiddenPlmns RemoteException: " + ex.getMessage()); } catch (NullPointerException ex) { @@ -7264,7 +7246,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony == null) return new String[0]; - return telephony.getPcscfAddress(apnType, getOpPackageName(), getFeatureId()); + return telephony.getPcscfAddress(apnType, getOpPackageName(), getAttributionTag()); } catch (RemoteException e) { return new String[0]; } @@ -7837,7 +7819,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } catch (RemoteException ex) { Rlog.e(TAG, "getAvailableNetworks RemoteException", ex); @@ -7892,7 +7874,7 @@ public class TelephonyManager { } } return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback, - getOpPackageName(), getFeatureId()); + getOpPackageName(), getAttributionTag()); } /** @@ -8646,7 +8628,7 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) - return telephony.isRadioOnWithFeature(getOpPackageName(), getFeatureId()); + return telephony.isRadioOnWithFeature(getOpPackageName(), getAttributionTag()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#isRadioOn", e); } @@ -8725,7 +8707,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public PinResult supplyPinReportPinResult(@NonNull String pin) { @@ -8750,7 +8731,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public PinResult supplyPukReportPinResult(@NonNull String puk, @NonNull String pin) { @@ -8942,7 +8922,10 @@ public class TelephonyManager { } /** - * Shut down all the live radios over all the slot index. + * Shut down all the live radios over all the slot indexes. + * + * <p>To know when the radio has completed powering off, use + * {@link PhoneStateListener#LISTEN_SERVICE_STATE LISTEN_SERVICE_STATE}. * * @hide */ @@ -8955,7 +8938,8 @@ public class TelephonyManager { telephony.shutdownMobileRadios(); } } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#shutdownMobileRadios", e); + Log.e(TAG, "Error calling ITelephony#shutdownAllRadios", e); + e.rethrowAsRuntimeException(); } } @@ -8974,7 +8958,8 @@ public class TelephonyManager { return telephony.needMobileRadioShutdown(); } } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#needMobileRadioShutdown", e); + Log.e(TAG, "Error calling ITelephony#isAnyRadioPoweredOn", e); + e.rethrowAsRuntimeException(); } return false; } @@ -9017,7 +9002,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getRadioPowerState(getSlotIndex(), mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // This could happen if binder process crashes. @@ -9408,7 +9393,7 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) - return telephony.isVideoCallingEnabled(getOpPackageName(), getFeatureId()); + return telephony.isVideoCallingEnabled(getOpPackageName(), getAttributionTag()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#isVideoCallingEnabled", e); } @@ -9425,7 +9410,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.canChangeDtmfToneLength(mSubId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#canChangeDtmfToneLength", e); @@ -9444,7 +9429,7 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.isWorldPhone(mSubId, getOpPackageName(), getFeatureId()); + return telephony.isWorldPhone(mSubId, getOpPackageName(), getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#isWorldPhone", e); @@ -10172,7 +10157,8 @@ public class TelephonyManager { ITelephony service = getITelephony(); if (service != null) { retval = service.getSubIdForPhoneAccountHandle( - phoneAccountHandle, mContext.getOpPackageName(), mContext.getFeatureId()); + phoneAccountHandle, mContext.getOpPackageName(), + mContext.getAttributionTag()); } } catch (RemoteException ex) { Log.e(TAG, "getSubscriptionId RemoteException", ex); @@ -10313,7 +10299,7 @@ public class TelephonyManager { ITelephony service = getITelephony(); if (service != null) { return service.getServiceStateForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e); @@ -11034,7 +11020,8 @@ public class TelephonyManager { try { ITelephony service = getITelephony(); if (service != null) { - return service.getClientRequestStats(getOpPackageName(), getFeatureId(), subId); + return service.getClientRequestStats(getOpPackageName(), getAttributionTag(), + subId); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#getClientRequestStats", e); @@ -11145,21 +11132,21 @@ public class TelephonyManager { } /** - * Checks whether cellular data connection is enabled in the device. + * Checks whether cellular data connection is allowed in the device. * - * Whether cellular data connection is enabled, meaning upon request whether will try to setup - * metered data connection considering all factors below: - * 1) User turned on data setting {@link #isDataEnabled}. - * 2) Carrier allows data to be on. - * 3) Network policy. - * And possibly others. - * - * @return {@code true} if the overall data connection is capable; {@code false} if not. + * <p>Whether cellular data connection is allowed considers all factors below: + * <UL> + * <LI>User turned on data setting {@link #isDataEnabled}.</LI> + * <LI>Carrier allows data to be on.</LI> + * <LI>Network policy.</LI> + * <LI>And possibly others.</LI> + * </UL> + * @return {@code true} if the overall data connection is allowed; {@code false} if not. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public boolean isDataConnectionEnabled() { + public boolean isDataConnectionAllowed() { boolean retVal = false; try { int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId()); @@ -11167,8 +11154,9 @@ public class TelephonyManager { if (telephony != null) retVal = telephony.isDataEnabled(subId); } catch (RemoteException e) { - Log.e(TAG, "Error isDataConnectionEnabled", e); + Log.e(TAG, "Error isDataConnectionAllowed", e); } catch (NullPointerException e) { + return false; } return retVal; } @@ -11319,7 +11307,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getNumberOfModemsWithSimultaneousDataConnections( - getSubId(), getOpPackageName(), getFeatureId()); + getSubId(), getOpPackageName(), getAttributionTag()); } } catch (RemoteException ex) { // This could happen if binder process crashes. @@ -11749,7 +11737,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getEmergencyNumberList(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } else { throw new IllegalStateException("telephony service is null."); } @@ -11804,7 +11792,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { emergencyNumberList = telephony.getEmergencyNumberList( - mContext.getOpPackageName(), mContext.getFeatureId()); + mContext.getOpPackageName(), mContext.getAttributionTag()); if (emergencyNumberList != null) { for (Integer subscriptionId : emergencyNumberList.keySet()) { List<EmergencyNumber> numberList = emergencyNumberList.get(subscriptionId); @@ -11897,7 +11885,7 @@ public class TelephonyManager { } /** - * A test API to return the emergency number db version. + * Returns the emergency number database version. * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} @@ -11906,6 +11894,7 @@ public class TelephonyManager { */ @TestApi @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion() { try { ITelephony telephony = getITelephony(); @@ -12112,13 +12101,13 @@ public class TelephonyManager { }) public int getPreferredOpportunisticDataSubscription() { String packageName = mContext != null ? mContext.getOpPackageName() : "<unknown>"; - String featureId = mContext != null ? mContext.getFeatureId() : null; + String attributionTag = mContext != null ? mContext.getAttributionTag() : null; int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; try { IOns iOpportunisticNetworkService = getIOns(); if (iOpportunisticNetworkService != null) { subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId( - packageName, featureId); + packageName, attributionTag); } } catch (RemoteException ex) { Rlog.e(TAG, "getPreferredDataSubscriptionId RemoteException", ex); @@ -12235,18 +12224,20 @@ public class TelephonyManager { /** * It indicates whether modem is enabled or not per slot. - * It's the corresponding status of {@link #enableModemForSlot}. + * It's the corresponding status of TelephonyManager.enableModemForSlot. * + * <p>Requires Permission: + * READ_PRIVILEGED_PHONE_STATE or + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * @param slotIndex which slot it's checking. - * @hide */ - @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isModemEnabledForSlot(int slotIndex) { try { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.isModemEnabledForSlot(slotIndex, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { Log.e(TAG, "enableModem RemoteException", ex); @@ -12351,7 +12342,7 @@ public class TelephonyManager { try { ITelephony service = getITelephony(); if (service != null) { - return service.isMultiSimSupported(getOpPackageName(), getFeatureId()); + return service.isMultiSimSupported(getOpPackageName(), getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "isMultiSimSupported RemoteException", e); @@ -12403,7 +12394,7 @@ public class TelephonyManager { ITelephony service = getITelephony(); if (service != null) { return service.doesSwitchMultiSimConfigTriggerReboot(getSubId(), - getOpPackageName(), getFeatureId()); + getOpPackageName(), getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "doesSwitchMultiSimConfigTriggerReboot RemoteException", e); @@ -12472,7 +12463,6 @@ public class TelephonyManager { * @throws {@link SecurityException} if the caller is not the system or phone process. * @hide */ - @SystemApi @TestApi // TODO: add new permission tag indicating that this is system-only. public @NonNull List<ApnSetting> getDevicePolicyOverrideApns(@NonNull Context context) { @@ -12503,7 +12493,6 @@ public class TelephonyManager { * @throws {@link SecurityException} if the caller is not the system or phone process. * @hide */ - @SystemApi @TestApi // TODO: add new permission tag indicating that this is system-only. public int addDevicePolicyOverrideApn(@NonNull Context context, @@ -12534,7 +12523,6 @@ public class TelephonyManager { * @throws {@link SecurityException} if the caller is not the system or phone process. * @hide */ - @SystemApi @TestApi // TODO: add new permission tag indicating that this is system-only. public boolean modifyDevicePolicyOverrideApn(@NonNull Context context, int apnId, diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index f5dfacc6a0be..bfb54b008cd8 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -18,7 +18,6 @@ package android.telephony.data; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.content.ContentValues; import android.database.Cursor; import android.hardware.radio.V1_5.ApnTypes; @@ -27,7 +26,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony; import android.provider.Telephony.Carriers; -import android.telephony.Annotation; import android.telephony.Annotation.ApnType; import android.telephony.Annotation.NetworkType; import android.telephony.ServiceState; @@ -126,6 +124,15 @@ public class ApnSetting implements Parcelable { /** Authentication type for PAP or CHAP. */ public static final int AUTH_TYPE_PAP_OR_CHAP = 3; + /** @hide */ + @IntDef({ + Telephony.Carriers.SKIP_464XLAT_DEFAULT, + Telephony.Carriers.SKIP_464XLAT_DISABLE, + Telephony.Carriers.SKIP_464XLAT_ENABLE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Skip464XlatStatus {} + /** * APN types for data connections. These are usage categories for an APN * entry. One APN entry may support multiple APN types, eg, a single APN @@ -139,7 +146,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_ALL_STRING = "*"; /** @@ -147,7 +153,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_DEFAULT_STRING = "default"; @@ -156,7 +161,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_MMS_STRING = "mms"; @@ -165,7 +169,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_SUPL_STRING = "supl"; /** @@ -173,7 +176,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_DUN_STRING = "dun"; /** @@ -181,7 +183,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_HIPRI_STRING = "hipri"; /** @@ -189,7 +190,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_FOTA_STRING = "fota"; /** @@ -197,7 +197,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_IMS_STRING = "ims"; /** @@ -205,7 +204,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_CBS_STRING = "cbs"; /** @@ -213,7 +211,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_IA_STRING = "ia"; /** @@ -222,7 +219,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_EMERGENCY_STRING = "emergency"; /** @@ -230,7 +226,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_MCX_STRING = "mcx"; /** @@ -238,7 +233,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_XCAP_STRING = "xcap"; @@ -745,7 +739,7 @@ public class ApnSetting implements Parcelable { * @return SKIP_464XLAT_DEFAULT, SKIP_464XLAT_DISABLE or SKIP_464XLAT_ENABLE * @hide */ - @Annotation.Skip464XlatStatus + @Skip464XlatStatus public int getSkip464Xlat() { return mSkip464Xlat; } @@ -1416,7 +1410,6 @@ public class ApnSetting implements Parcelable { * @return comma delimited list of APN types. * @hide */ - @SystemApi @NonNull public static String getApnTypesStringFromBitmask(int apnTypeBitmask) { List<String> types = new ArrayList<>(); @@ -2065,7 +2058,7 @@ public class ApnSetting implements Parcelable { * @param skip464xlat skip464xlat for this APN. * @hide */ - public Builder setSkip464Xlat(@Annotation.Skip464XlatStatus int skip464xlat) { + public Builder setSkip464Xlat(@Skip464XlatStatus int skip464xlat) { this.mSkip464Xlat = skip464xlat; return this; } diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 3341fa74d672..4b5303f42be0 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -294,8 +294,15 @@ public class ImsMmTelManager implements RegistrationManager { throw new IllegalArgumentException("Must include a non-null Executor."); } c.setExecutor(executor); + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new ImsException("Could not find Telephony Service.", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + try { - getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder()); + iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder()); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -331,8 +338,15 @@ public class ImsMmTelManager implements RegistrationManager { throw new IllegalArgumentException("Must include a non-null Executor."); } c.setExecutor(executor); + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new ImsException("Could not find Telephony Service.", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + try { - getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder()); + iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder()); } catch (ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException | IllegalStateException e) { @@ -361,8 +375,14 @@ public class ImsMmTelManager implements RegistrationManager { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder()); + iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -387,8 +407,14 @@ public class ImsMmTelManager implements RegistrationManager { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder()); + iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -409,8 +435,14 @@ public class ImsMmTelManager implements RegistrationManager { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() { + iTelephony.getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() { @Override public void accept(int result) { executor.execute(() -> stateCallback.accept(result)); @@ -443,8 +475,14 @@ public class ImsMmTelManager implements RegistrationManager { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().getImsMmTelRegistrationTransportType(mSubId, + iTelephony.getImsMmTelRegistrationTransportType(mSubId, new IIntegerConsumer.Stub() { @Override public void accept(int result) { @@ -506,8 +544,15 @@ public class ImsMmTelManager implements RegistrationManager { throw new IllegalArgumentException("Must include a non-null Executor."); } c.setExecutor(executor); + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new ImsException("Could not find Telephony Service.", + ImsException.CODE_ERROR_INVALID_SUBSCRIPTION); + } + try { - getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder()); + iTelephony.registerMmTelCapabilityCallback(mSubId, c.getBinder()); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -553,8 +598,14 @@ public class ImsMmTelManager implements RegistrationManager { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().unregisterMmTelCapabilityCallback(mSubId, c.getBinder()); + iTelephony.unregisterMmTelCapabilityCallback(mSubId, c.getBinder()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -599,8 +650,13 @@ public class ImsMmTelManager implements RegistrationManager { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isAdvancedCallingSettingEnabled() { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - return getITelephony().isAdvancedCallingSettingEnabled(mSubId); + return iTelephony.isAdvancedCallingSettingEnabled(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -640,8 +696,13 @@ public class ImsMmTelManager implements RegistrationManager { @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi @TestApi public void setAdvancedCallingSettingEnabled(boolean isEnabled) { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled); + iTelephony.setAdvancedCallingSettingEnabled(mSubId, isEnabled); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -680,8 +741,13 @@ public class ImsMmTelManager implements RegistrationManager { @SystemApi @TestApi public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - return getITelephony().isCapable(mSubId, capability, imsRegTech); + return iTelephony.isCapable(mSubId, capability, imsRegTech); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -709,8 +775,13 @@ public class ImsMmTelManager implements RegistrationManager { @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - return getITelephony().isAvailable(mSubId, capability, imsRegTech); + return iTelephony.isAvailable(mSubId, capability, imsRegTech); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -744,6 +815,13 @@ public class ImsMmTelManager implements RegistrationManager { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new ImsException("Could not find Telephony Service.", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + try { getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() { @Override @@ -788,8 +866,13 @@ public class ImsMmTelManager implements RegistrationManager { android.Manifest.permission.READ_PRECISE_PHONE_STATE}) @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). public boolean isVtSettingEnabled() { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - return getITelephony().isVtSettingEnabled(mSubId); + return iTelephony.isVtSettingEnabled(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -813,8 +896,13 @@ public class ImsMmTelManager implements RegistrationManager { @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean isEnabled) { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().setVtSettingEnabled(mSubId, isEnabled); + iTelephony.setVtSettingEnabled(mSubId, isEnabled); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -853,8 +941,13 @@ public class ImsMmTelManager implements RegistrationManager { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled() { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - return getITelephony().isVoWiFiSettingEnabled(mSubId); + return iTelephony.isVoWiFiSettingEnabled(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -879,8 +972,13 @@ public class ImsMmTelManager implements RegistrationManager { @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean isEnabled) { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled); + iTelephony.setVoWiFiSettingEnabled(mSubId, isEnabled); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -921,8 +1019,13 @@ public class ImsMmTelManager implements RegistrationManager { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiRoamingSettingEnabled() { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId); + return iTelephony.isVoWiFiRoamingSettingEnabled(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -948,8 +1051,13 @@ public class ImsMmTelManager implements RegistrationManager { @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled); + iTelephony.setVoWiFiRoamingSettingEnabled(mSubId, isEnabled); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -980,8 +1088,13 @@ public class ImsMmTelManager implements RegistrationManager { @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean isCapable, int mode) { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode); + iTelephony.setVoWiFiNonPersistent(mSubId, isCapable, mode); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -1025,8 +1138,13 @@ public class ImsMmTelManager implements RegistrationManager { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public @WiFiCallingMode int getVoWiFiModeSetting() { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - return getITelephony().getVoWiFiModeSetting(mSubId); + return iTelephony.getVoWiFiModeSetting(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -1054,8 +1172,13 @@ public class ImsMmTelManager implements RegistrationManager { @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(@WiFiCallingMode int mode) { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().setVoWiFiModeSetting(mSubId, mode); + iTelephony.setVoWiFiModeSetting(mSubId, mode); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -1085,8 +1208,13 @@ public class ImsMmTelManager implements RegistrationManager { @SystemApi @TestApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @WiFiCallingMode int getVoWiFiRoamingModeSetting() { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - return getITelephony().getVoWiFiRoamingModeSetting(mSubId); + return iTelephony.getVoWiFiRoamingModeSetting(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -1116,8 +1244,13 @@ public class ImsMmTelManager implements RegistrationManager { @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode); + iTelephony.setVoWiFiRoamingModeSetting(mSubId, mode); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -1145,8 +1278,13 @@ public class ImsMmTelManager implements RegistrationManager { @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean isEnabled) { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - getITelephony().setRttCapabilitySetting(mSubId, isEnabled); + iTelephony.setRttCapabilitySetting(mSubId, isEnabled); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -1186,8 +1324,13 @@ public class ImsMmTelManager implements RegistrationManager { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isTtyOverVolteEnabled() { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + try { - return getITelephony().isTtyOverVolteEnabled(mSubId); + return iTelephony.isTtyOverVolteEnabled(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. @@ -1223,8 +1366,15 @@ public class ImsMmTelManager implements RegistrationManager { if (callback == null) { throw new IllegalArgumentException("Must include a non-null Consumer."); } + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new ImsException("Could not find Telephony Service.", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + try { - getITelephony().getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() { + iTelephony.getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() { @Override public void accept(int result) { executor.execute(() -> callback.accept(result)); @@ -1243,9 +1393,6 @@ public class ImsMmTelManager implements RegistrationManager { .getTelephonyServiceManager() .getTelephonyServiceRegisterer() .get()); - if (binder == null) { - throw new RuntimeException("Could not find Telephony Service."); - } return binder; } } diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 30306c7f9dea..05ab6bd75878 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -291,7 +291,7 @@ public class RcsUceAdapter { try { imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(), - mContext.getFeatureId(), contactNumbers, internalCallback); + mContext.getAttributionTag(), contactNumbers, internalCallback); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e); throw new ImsException("Remote IMS Service is not available", @@ -352,7 +352,7 @@ public class RcsUceAdapter { try { // Telephony.SimInfo#IMS_RCS_UCE_ENABLED can also be used to listen to changes to this. return imsRcsController.isUceSettingEnabled(mSubId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#isUceSettingEnabled", e); throw new ImsException("Remote IMS Service is not available", diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java index 59049160e885..c7e5a5ea3311 100644 --- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java +++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java @@ -322,7 +322,7 @@ public class MemoryUsageTest extends InstrumentationTestCase { mAtm.startActivityAndWait(null, getInstrumentation().getContext().getBasePackageName(), - getInstrumentation().getContext().getFeatureId(), mLaunchIntent, + getInstrumentation().getContext().getAttributionTag(), mLaunchIntent, mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null, UserHandle.USER_CURRENT_OR_SELF); } catch (RemoteException e) { diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 819fc020e397..8cc8cf4d2a97 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -1063,52 +1063,6 @@ public class PackageWatchdogTest { assertThat(bootObserver2.mitigatedBootLoop()).isFalse(); } - /** - * Test to verify that Package Watchdog syncs health check requests with the controller - * correctly, and that the requests are only synced when the set of observed packages - * changes. - */ - @Test - public void testSyncHealthCheckRequests() { - TestController testController = spy(TestController.class); - testController.setSupportedPackages(List.of(APP_A, APP_B, APP_C)); - PackageWatchdog watchdog = createWatchdog(testController, true); - - TestObserver testObserver1 = new TestObserver(OBSERVER_NAME_1); - watchdog.registerHealthObserver(testObserver1); - watchdog.startObservingHealth(testObserver1, List.of(APP_A), LONG_DURATION); - mTestLooper.dispatchAll(); - - TestObserver testObserver2 = new TestObserver(OBSERVER_NAME_2); - watchdog.registerHealthObserver(testObserver2); - watchdog.startObservingHealth(testObserver2, List.of(APP_B), LONG_DURATION); - mTestLooper.dispatchAll(); - - TestObserver testObserver3 = new TestObserver(OBSERVER_NAME_3); - watchdog.registerHealthObserver(testObserver3); - watchdog.startObservingHealth(testObserver3, List.of(APP_C), LONG_DURATION); - mTestLooper.dispatchAll(); - - watchdog.unregisterHealthObserver(testObserver1); - mTestLooper.dispatchAll(); - - watchdog.unregisterHealthObserver(testObserver2); - mTestLooper.dispatchAll(); - - watchdog.unregisterHealthObserver(testObserver3); - mTestLooper.dispatchAll(); - - List<Set> expectedSyncRequests = List.of( - Set.of(APP_A), - Set.of(APP_A, APP_B), - Set.of(APP_A, APP_B, APP_C), - Set.of(APP_B, APP_C), - Set.of(APP_C), - Set.of() - ); - assertThat(testController.getSyncRequests()).isEqualTo(expectedSyncRequests); - } - private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() @@ -1265,7 +1219,6 @@ public class PackageWatchdogTest { private Consumer<String> mPassedConsumer; private Consumer<List<PackageConfig>> mSupportedConsumer; private Runnable mNotifySyncRunnable; - private List<Set> mSyncRequests = new ArrayList<>(); @Override public void setEnabled(boolean enabled) { @@ -1285,7 +1238,6 @@ public class PackageWatchdogTest { @Override public void syncRequests(Set<String> packages) { - mSyncRequests.add(packages); mRequestedPackages.clear(); if (mIsEnabled) { packages.retainAll(mSupportedPackages); @@ -1316,10 +1268,6 @@ public class PackageWatchdogTest { return Collections.emptyList(); } } - - public List<Set> getSyncRequests() { - return mSyncRequests; - } } private static class TestClock implements PackageWatchdog.SystemClock { diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java index 8e6f1985a7d9..e41517085c36 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java @@ -16,11 +16,15 @@ package com.google.android.test.windowinsetstests; +import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static java.lang.Math.max; import static java.lang.Math.min; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -37,6 +41,8 @@ import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimation.Callback; import android.view.WindowInsetsAnimationControlListener; import android.view.WindowInsetsAnimationController; +import android.view.WindowInsetsController; +import android.view.WindowInsetsController.OnControllableInsetsChangedListener; import android.view.animation.LinearInterpolator; import android.widget.LinearLayout; @@ -82,8 +88,8 @@ public class WindowInsetsActivity extends AppCompatActivity { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mDown = event.getY(); - mDownInsets = v.getRootWindowInsets().getInsets(Type.ime()); - mShownAtDown = v.getRootWindowInsets().isVisible(Type.ime()); + mDownInsets = v.getRootWindowInsets().getInsets(ime()); + mShownAtDown = v.getRootWindowInsets().isVisible(ime()); mRequestedController = false; mCurrentRequest = null; break; @@ -94,7 +100,7 @@ public class WindowInsetsActivity extends AppCompatActivity { > mViewConfiguration.getScaledTouchSlop() && !mRequestedController) { mRequestedController = true; - v.getWindowInsetsController().controlWindowInsetsAnimation(Type.ime(), + v.getWindowInsetsController().controlWindowInsetsAnimation(ime(), 1000, new LinearInterpolator(), mCurrentRequest = new WindowInsetsAnimationControlListener() { @Override @@ -189,6 +195,51 @@ public class WindowInsetsActivity extends AppCompatActivity { getWindow().getDecorView().post(() -> getWindow().setDecorFitsSystemWindows(false)); } + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + getWindow().getInsetsController().addOnControllableInsetsChangedListener( + new OnControllableInsetsChangedListener() { + + boolean hasControl = false; + @Override + public void onControllableInsetsChanged(WindowInsetsController controller, + int types) { + if ((types & ime()) != 0 && !hasControl) { + hasControl = true; + controller.controlWindowInsetsAnimation(ime(), -1, + new LinearInterpolator(), + new WindowInsetsAnimationControlListener() { + @Override + public void onReady( + WindowInsetsAnimationController controller, + int types) { + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + anim.setDuration(1500); + anim.addUpdateListener(animation + -> controller.setInsetsAndAlpha( + controller.getShownStateInsets(), + (float) animation.getAnimatedValue(), + anim.getAnimatedFraction())); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + controller.finish(true); + } + }); + anim.start(); + } + + @Override + public void onCancelled() { + } + }); + } + } + }); + } + static class Transition { private int mEndBottom; private int mStartBottom; @@ -200,7 +251,7 @@ public class WindowInsetsActivity extends AppCompatActivity { } void onPrepare(WindowInsetsAnimation animation) { - if ((animation.getTypeMask() & Type.ime()) != 0) { + if ((animation.getTypeMask() & ime()) != 0) { mInsetsAnimation = animation; } mStartBottom = mView.getBottom(); diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java index fe51b3af4d72..1658262c17f6 100644 --- a/tests/net/common/java/android/net/RouteInfoTest.java +++ b/tests/net/common/java/android/net/RouteInfoTest.java @@ -19,19 +19,40 @@ package android.net; import static android.net.RouteInfo.RTN_UNREACHABLE; import static com.android.testutils.MiscAssertsKt.assertEqualBothWays; +import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; -import android.test.suitebuilder.annotation.SmallTest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; -import junit.framework.TestCase; +import android.os.Build; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; -public class RouteInfoTest extends TestCase { +@RunWith(AndroidJUnit4.class) +@SmallTest +public class RouteInfoTest { + @Rule + public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); + + private static final int INVALID_ROUTE_TYPE = -1; private InetAddress Address(String addr) { return InetAddress.parseNumericAddress(addr); @@ -41,15 +62,32 @@ public class RouteInfoTest extends TestCase { return new IpPrefix(prefix); } - @SmallTest + @Test public void testConstructor() { RouteInfo r; - // Invalid input. try { r = new RouteInfo((IpPrefix) null, null, "rmnet0"); fail("Expected RuntimeException: destination and gateway null"); - } catch(RuntimeException e) {} + } catch (RuntimeException e) { } + + try { + r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "rmnet0", + INVALID_ROUTE_TYPE); + fail("Invalid route type should cause exception"); + } catch (IllegalArgumentException e) { } + + try { + r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("192.0.2.1"), "rmnet0", + RTN_UNREACHABLE); + fail("Address family mismatch should cause exception"); + } catch (IllegalArgumentException e) { } + + try { + r = new RouteInfo(Prefix("0.0.0.0/0"), Address("2001:db8::1"), "rmnet0", + RTN_UNREACHABLE); + fail("Address family mismatch should cause exception"); + } catch (IllegalArgumentException e) { } // Null destination is default route. r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null); @@ -74,6 +112,7 @@ public class RouteInfoTest extends TestCase { assertNull(r.getInterface()); } + @Test public void testMatches() { class PatchedRouteInfo { private final RouteInfo mRouteInfo; @@ -113,6 +152,7 @@ public class RouteInfoTest extends TestCase { assertFalse(ipv4Default.matches(Address("2001:db8::f00"))); } + @Test public void testEquals() { // IPv4 RouteInfo r1 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0"); @@ -146,6 +186,7 @@ public class RouteInfoTest extends TestCase { assertNotEqualEitherWay(r1, r3); } + @Test public void testHostAndDefaultRoutes() { RouteInfo r; @@ -228,6 +269,7 @@ public class RouteInfoTest extends TestCase { assertFalse(r.isIPv6Default()); } + @Test public void testTruncation() { LinkAddress l; RouteInfo r; @@ -244,6 +286,7 @@ public class RouteInfoTest extends TestCase { // Make sure that creating routes to multicast addresses doesn't throw an exception. Even though // there's nothing we can do with them, we don't want to crash if, e.g., someone calls // requestRouteToHostAddress("230.0.0.0", MOBILE_HIPRI); + @Test public void testMulticastRoute() { RouteInfo r; r = new RouteInfo(Prefix("230.0.0.0/32"), Address("192.0.2.1"), "wlan0"); @@ -251,16 +294,36 @@ public class RouteInfoTest extends TestCase { // No exceptions? Good. } + @Test public void testParceling() { RouteInfo r; + r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), null); + assertParcelingIsLossless(r); + r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); + assertParcelingIsLossless(r); + r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0", RTN_UNREACHABLE); + assertParcelingIsLossless(r); + } - r = new RouteInfo(Prefix("::/0"), Address("2001:db8::"), null); + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testMtuParceling() { + final RouteInfo r = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::"), "testiface", + RTN_UNREACHABLE, 1450 /* mtu */); assertParcelingIsLossless(r); + } - r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); - assertParcelSane(r, 7); + @Test @IgnoreAfter(Build.VERSION_CODES.Q) + public void testFieldCount_Q() { + assertFieldCountEquals(6, RouteInfo.class); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testFieldCount() { + // Make sure any new field is covered by the above parceling tests when changing this number + assertFieldCountEquals(7, RouteInfo.class); } + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) public void testMtu() { RouteInfo r; r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0", diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/tests/net/common/java/android/net/util/SocketUtilsTest.kt index 9c7cfb0c716e..aaf97f36889b 100644 --- a/tests/net/common/java/android/net/util/SocketUtilsTest.kt +++ b/tests/net/common/java/android/net/util/SocketUtilsTest.kt @@ -14,8 +14,9 @@ * limitations under the License. */ -package android.net.util; +package android.net.util +import android.os.Build import android.system.NetlinkSocketAddress import android.system.Os import android.system.OsConstants.AF_INET @@ -26,18 +27,26 @@ import android.system.OsConstants.SOCK_DGRAM import android.system.PacketSocketAddress import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Assert.fail +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith private const val TEST_INDEX = 123 private const val TEST_PORT = 555 +private const val FF_BYTE = 0xff.toByte() + @RunWith(AndroidJUnit4::class) @SmallTest class SocketUtilsTest { + @Rule @JvmField + val ignoreRule = DevSdkIgnoreRule() + @Test fun testMakeNetlinkSocketAddress() { val nlAddress = SocketUtils.makeNetlinkSocketAddress(TEST_PORT, RTMGRP_NEIGH) @@ -50,16 +59,21 @@ class SocketUtilsTest { } @Test - fun testMakePacketSocketAddress() { + fun testMakePacketSocketAddress_Q() { val pkAddress = SocketUtils.makePacketSocketAddress(ETH_P_ALL, TEST_INDEX) assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress) - val ff = 0xff.toByte() - val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX, - byteArrayOf(ff, ff, ff, ff, ff, ff)) + val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX, ByteArray(6) { FF_BYTE }) assertTrue("Not PacketSocketAddress object", pkAddress2 is PacketSocketAddress) } + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testMakePacketSocketAddress() { + val pkAddress = SocketUtils.makePacketSocketAddress( + ETH_P_ALL, TEST_INDEX, ByteArray(6) { FF_BYTE }) + assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress) + } + @Test fun testCloseSocket() { // Expect no exception happening with null object. diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 8c0c36b89bf9..6985415a6a37 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -23,8 +23,6 @@ import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport; -import static android.net.ConnectivityDiagnosticsManager.DataStallReport; import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL; @@ -100,6 +98,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; 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.startsWith; import static org.mockito.Matchers.anyInt; @@ -6870,8 +6869,13 @@ public class ConnectivityServiceTest { HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); // Verify onConnectivityReport fired - verify(mConnectivityDiagnosticsCallback) - .onConnectivityReport(any(ConnectivityReport.class)); + verify(mConnectivityDiagnosticsCallback).onConnectivityReport( + argThat(report -> { + final NetworkCapabilities nc = report.getNetworkCapabilities(); + return nc.getUids() == null + && nc.getAdministratorUids().isEmpty() + && nc.getOwnerUid() == Process.INVALID_UID; + })); } @Test @@ -6886,7 +6890,13 @@ public class ConnectivityServiceTest { HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); // Verify onDataStallSuspected fired - verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(any(DataStallReport.class)); + verify(mConnectivityDiagnosticsCallback).onDataStallSuspected( + argThat(report -> { + final NetworkCapabilities nc = report.getNetworkCapabilities(); + return nc.getUids() == null + && nc.getAdministratorUids().isEmpty() + && nc.getOwnerUid() == Process.INVALID_UID; + })); } @Test diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index a9e0b9abba9c..36deca3e37b7 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -64,6 +64,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.app.AlarmManager; import android.app.usage.NetworkStatsManager; import android.content.Context; @@ -163,7 +164,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private @Mock IBinder mBinder; private @Mock AlarmManager mAlarmManager; private HandlerThread mHandlerThread; - private Handler mHandler; private NetworkStatsService mService; private INetworkStatsSession mSession; @@ -192,15 +192,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); - mService = new NetworkStatsService( - mServiceContext, mNetManager, mAlarmManager, wakeLock, mClock, - mServiceContext.getSystemService(TelephonyManager.class), mSettings, - mStatsFactory, new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir)); mHandlerThread = new HandlerThread("HandlerThread"); - mHandlerThread.start(); - Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService); - mHandler = new Handler(mHandlerThread.getLooper(), callback); - mService.setHandler(mHandler, callback); + final NetworkStatsService.Dependencies deps = makeDependencies(); + mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock, + mClock, mServiceContext.getSystemService(TelephonyManager.class), mSettings, + mStatsFactory, new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir), deps); mElapsedRealtime = 0L; @@ -217,11 +213,21 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // catch INetworkManagementEventObserver during systemReady() ArgumentCaptor<INetworkManagementEventObserver> networkObserver = - ArgumentCaptor.forClass(INetworkManagementEventObserver.class); + ArgumentCaptor.forClass(INetworkManagementEventObserver.class); verify(mNetManager).registerObserver(networkObserver.capture()); mNetworkObserver = networkObserver.getValue(); } + @NonNull + private NetworkStatsService.Dependencies makeDependencies() { + return new NetworkStatsService.Dependencies() { + @Override + public HandlerThread makeHandlerThread() { + return mHandlerThread; + } + }; + } + @After public void tearDown() throws Exception { IoUtils.deleteContents(mStatsDir); @@ -234,6 +240,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mSession.close(); mService = null; + + mHandlerThread.quitSafely(); } @Test @@ -939,9 +947,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { long minThresholdInBytes = 2 * 1024 * 1024; // 2 MB assertEquals(minThresholdInBytes, request.thresholdInBytes); - // Send dummy message to make sure that any previous message has been handled - mHandler.sendMessage(mHandler.obtainMessage(-1)); - HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT); + HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); // Make sure that the caller binder gets connected verify(mBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); @@ -1077,7 +1083,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // Simulates alert quota of the provider has been reached. cb.onAlertReached(); - HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT); + HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); // Verifies that polling is triggered by alert reached. provider.expectStatsUpdate(0 /* unused */); @@ -1294,9 +1300,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private void forcePollAndWaitForIdle() { mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); - // Send dummy message to make sure that any previous message has been handled - mHandler.sendMessage(mHandler.obtainMessage(-1)); - HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT); + HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); } static class LatchedHandler extends Handler { diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 5aa32f868104..bcfce663db00 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -366,8 +366,12 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, }); manifest_action["instrumentation"]["meta-data"] = meta_data_action; + // TODO moltmann: Remove manifest_action["feature"]; manifest_action["feature"]["inherit-from"]; + + manifest_action["attribution"]; + manifest_action["attribution"]["inherit-from"]; manifest_action["original-package"]; manifest_action["overlay"]; manifest_action["protected-broadcast"]; diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp index cbf6fe88e565..b1e2487c54fe 100644 --- a/tools/stats_log_api_gen/Android.bp +++ b/tools/stats_log_api_gen/Android.bp @@ -120,12 +120,6 @@ cc_library { "liblog", "libcutils", ], - apex_available: [ - "//apex_available:platform", - //TODO(b/149781190): Remove this once statsd no longer depends on libstatslog - "com.android.os.statsd", - "test_com.android.os.statsd", - ], target: { android: { shared_libs: ["libstatssocket"], diff --git a/wifi/Android.bp b/wifi/Android.bp index 91174d3c3be2..f4d28817a7f2 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -177,14 +177,14 @@ droidstubs { java_library { name: "framework-wifi-stubs-publicapi", srcs: [":framework-wifi-stubs-srcs-publicapi"], - sdk_version: "module_current", + sdk_version: "current", installable: false, } java_library { name: "framework-wifi-stubs-systemapi", srcs: [":framework-wifi-stubs-srcs-systemapi"], - sdk_version: "module_current", + sdk_version: "system_current", libs: ["framework-annotations-lib"], installable: false, } diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index 2b4762320f52..a269e17a8cff 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -35,7 +35,6 @@ import java.lang.annotation.RetentionPolicy; import java.nio.charset.CharsetEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -211,7 +210,7 @@ public final class SoftApConfiguration implements Parcelable { * Delay in milliseconds before shutting down soft AP when * there are no connected devices. */ - private final int mShutdownTimeoutMillis; + private final long mShutdownTimeoutMillis; /** * THe definition of security type OPEN. @@ -247,7 +246,7 @@ public final class SoftApConfiguration implements Parcelable { private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid, @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel, @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled, - int shutdownTimeoutMillis, boolean clientControlByUser, + long shutdownTimeoutMillis, boolean clientControlByUser, @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList) { mSsid = ssid; mBssid = bssid; @@ -327,7 +326,7 @@ public final class SoftApConfiguration implements Parcelable { dest.writeInt(mSecurityType); dest.writeInt(mMaxNumberOfClients); dest.writeBoolean(mAutoShutdownEnabled); - dest.writeInt(mShutdownTimeoutMillis); + dest.writeLong(mShutdownTimeoutMillis); dest.writeBoolean(mClientControlByUser); dest.writeTypedList(mBlockedClientList); dest.writeTypedList(mAllowedClientList); @@ -346,7 +345,7 @@ public final class SoftApConfiguration implements Parcelable { in.readString(), in.readParcelable(MacAddress.class.getClassLoader()), in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(), - in.readInt(), in.readBoolean(), in.readInt(), in.readBoolean(), + in.readInt(), in.readBoolean(), in.readLong(), in.readBoolean(), in.createTypedArrayList(MacAddress.CREATOR), in.createTypedArrayList(MacAddress.CREATOR)); } @@ -454,19 +453,19 @@ public final class SoftApConfiguration implements Parcelable { /** * Returns the shutdown timeout in milliseconds. * The Soft AP will shutdown when there are no devices associated to it for - * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(int)}. + * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(long)}. * * @hide */ @SystemApi - public int getShutdownTimeoutMillis() { + public long getShutdownTimeoutMillis() { return mShutdownTimeoutMillis; } /** * Returns a flag indicating whether clients need to be pre-approved by the user. * (true: authorization required) or not (false: not required). - * {@link Builder#enableClientControlByUser(Boolean)}. + * {@link Builder#setClientControlByUserEnabled(Boolean)}. * * @hide */ @@ -478,7 +477,7 @@ public final class SoftApConfiguration implements Parcelable { /** * Returns List of clients which aren't allowed to associate to the AP. * - * Clients are configured using {@link Builder#setClientList(List, List)} + * Clients are configured using {@link Builder#setBlockedClientList(List)} * * @hide */ @@ -490,7 +489,7 @@ public final class SoftApConfiguration implements Parcelable { /** * List of clients which are allowed to associate to the AP. - * Clients are configured using {@link Builder#setClientList(List, List)} + * Clients are configured using {@link Builder#setAllowedClientList(List)} * * @hide */ @@ -575,7 +574,7 @@ public final class SoftApConfiguration implements Parcelable { private int mMaxNumberOfClients; private int mSecurityType; private boolean mAutoShutdownEnabled; - private int mShutdownTimeoutMillis; + private long mShutdownTimeoutMillis; private boolean mClientControlByUser; private List<MacAddress> mBlockedClientList; private List<MacAddress> mAllowedClientList; @@ -627,6 +626,11 @@ public final class SoftApConfiguration implements Parcelable { */ @NonNull public SoftApConfiguration build() { + for (MacAddress client : mAllowedClientList) { + if (mBlockedClientList.contains(client)) { + throw new IllegalArgumentException("A MacAddress exist in both client list"); + } + } return new SoftApConfiguration(mSsid, mBssid, mPassphrase, mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser, @@ -835,7 +839,7 @@ public final class SoftApConfiguration implements Parcelable { * @param enable true to enable, false to disable. * @return Builder for chaining. * - * @see #setShutdownTimeoutMillis(int) + * @see #setShutdownTimeoutMillis(long) */ @NonNull public Builder setAutoShutdownEnabled(boolean enable) { @@ -862,7 +866,7 @@ public final class SoftApConfiguration implements Parcelable { * @see #setAutoShutdownEnabled(boolean) */ @NonNull - public Builder setShutdownTimeoutMillis(@IntRange(from = 0) int timeoutMillis) { + public Builder setShutdownTimeoutMillis(@IntRange(from = 0) long timeoutMillis) { if (timeoutMillis < 0) { throw new IllegalArgumentException("Invalid timeout value"); } @@ -878,7 +882,7 @@ public final class SoftApConfiguration implements Parcelable { * * If manual user control is enabled then clients will be accepted, rejected, or require * a user approval based on the configuration provided by - * {@link #setClientList(List, List)}. + * {@link #setBlockedClientList(List)} and {@link #setAllowedClientList(List)}. * * <p> * This method requires hardware support. Hardware support can be determined using @@ -898,26 +902,48 @@ public final class SoftApConfiguration implements Parcelable { * @return Builder for chaining. */ @NonNull - public Builder enableClientControlByUser(boolean enabled) { + public Builder setClientControlByUserEnabled(boolean enabled) { mClientControlByUser = enabled; return this; } /** - * This method together with {@link enableClientControlByUser(boolean)} control client - * connections to the AP. If {@link enableClientControlByUser(false)} is configured than + * This method together with {@link setClientControlByUserEnabled(boolean)} control client + * connections to the AP. If client control by user is disabled using the above method then * this API has no effect and clients are allowed to associate to the AP (within limit of * max number of clients). * - * If {@link enableClientControlByUser(true)} is configured then this API configures - * 2 lists: + * If client control by user is enabled then this API configures the list of clients + * which are explicitly allowed. These are auto-accepted. + * + * All other clients which attempt to associate, whose MAC addresses are on neither list, + * are: * <ul> - * <li>List of clients which are blocked. These are rejected.</li> - * <li>List of clients which are explicitly allowed. These are auto-accepted.</li> + * <li>Rejected</li> + * <li>A callback {@link WifiManager.SoftApCallback#onBlockedClientConnecting(WifiClient)} + * is issued (which allows the user to add them to the allowed client list if desired).<li> * </ul> * - * <p> + * @param allowedClientList list of clients which are allowed to associate to the AP + * without user pre-approval. + * @return Builder for chaining. + */ + @NonNull + public Builder setAllowedClientList(@NonNull List<MacAddress> allowedClientList) { + mAllowedClientList = new ArrayList<>(allowedClientList); + return this; + } + + /** + * This method together with {@link setClientControlByUserEnabled(boolean)} control client + * connections to the AP. If client control by user is disabled using the above method then + * this API has no effect and clients are allowed to associate to the AP (within limit of + * max number of clients). + * + * If client control by user is enabled then this API this API configures the list of + * clients which are blocked. These are rejected. + * * All other clients which attempt to associate, whose MAC addresses are on neither list, * are: * <ul> @@ -927,23 +953,11 @@ public final class SoftApConfiguration implements Parcelable { * </ul> * * @param blockedClientList list of clients which are not allowed to associate to the AP. - * @param allowedClientList list of clients which are allowed to associate to the AP - * without user pre-approval. * @return Builder for chaining. */ @NonNull - public Builder setClientList(@NonNull List<MacAddress> blockedClientList, - @NonNull List<MacAddress> allowedClientList) { + public Builder setBlockedClientList(@NonNull List<MacAddress> blockedClientList) { mBlockedClientList = new ArrayList<>(blockedClientList); - mAllowedClientList = new ArrayList<>(allowedClientList); - Iterator<MacAddress> iterator = mAllowedClientList.iterator(); - while (iterator.hasNext()) { - MacAddress client = iterator.next(); - int index = mBlockedClientList.indexOf(client); - if (index != -1) { - throw new IllegalArgumentException("A MacAddress exist in both list"); - } - } return this; } } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index e0b433d388eb..ba68d170364c 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -635,13 +635,6 @@ public class WifiConfiguration implements Parcelable { public String preSharedKey; /** - * Optional SAE Password Id for use with WPA3-SAE. It is an ASCII string. - * @hide - */ - @SystemApi - public @Nullable String saePasswordId; - - /** * Four WEP keys. For each of the four values, provide either an ASCII * string enclosed in double quotation marks (e.g., {@code "abcdef"}), * a string of hex digits (e.g., {@code 0102030405}), or an empty string @@ -2334,9 +2327,6 @@ public class WifiConfiguration implements Parcelable { sbuf.append('*'); } - sbuf.append('\n').append(" SAE Password Id: "); - sbuf.append(this.saePasswordId); - sbuf.append("\nEnterprise config:\n"); sbuf.append(enterpriseConfig); @@ -2731,7 +2721,6 @@ public class WifiConfiguration implements Parcelable { providerFriendlyName = source.providerFriendlyName; isHomeProviderNetwork = source.isHomeProviderNetwork; preSharedKey = source.preSharedKey; - saePasswordId = source.saePasswordId; mNetworkSelectionStatus.copy(source.getNetworkSelectionStatus()); apBand = source.apBand; @@ -2819,7 +2808,6 @@ public class WifiConfiguration implements Parcelable { dest.writeLong(roamingConsortiumId); } dest.writeString(preSharedKey); - dest.writeString(saePasswordId); for (String wepKey : wepKeys) { dest.writeString(wepKey); } @@ -2895,7 +2883,6 @@ public class WifiConfiguration implements Parcelable { config.roamingConsortiumIds[i] = in.readLong(); } config.preSharedKey = in.readString(); - config.saePasswordId = in.readString(); for (int i = 0; i < config.wepKeys.length; i++) { config.wepKeys[i] = in.readString(); } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 9e29a7a06706..e1acaf83c227 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -727,8 +727,9 @@ public class WifiManager { /** * If Soft Ap client is blocked, this reason code means that client doesn't exist in the - * specified configuration {@link SoftApConfiguration.Builder#setClientList(List, List)} - * and the {@link SoftApConfiguration.Builder#enableClientControlByUser(true)} + * specified configuration {@link SoftApConfiguration.Builder#setBlockedClientList(List)} + * and {@link SoftApConfiguration.Builder#setAllowedClientList(List)} + * and the {@link SoftApConfiguration.Builder#setClientControlByUserEnabled(boolean)} * is configured as well. * @hide */ @@ -1352,7 +1353,7 @@ public class WifiManager { try { ParceledListSlice<WifiConfiguration> parceledList = mService.getConfiguredNetworks(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); if (parceledList == null) { return Collections.emptyList(); } @@ -1369,7 +1370,7 @@ public class WifiManager { try { ParceledListSlice<WifiConfiguration> parceledList = mService.getPrivilegedConfiguredNetworks(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); if (parceledList == null) { return Collections.emptyList(); } @@ -1898,7 +1899,7 @@ public class WifiManager { @NonNull List<WifiNetworkSuggestion> networkSuggestions) { try { return mService.addNetworkSuggestions( - networkSuggestions, mContext.getOpPackageName(), mContext.getFeatureId()); + networkSuggestions, mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2680,8 +2681,8 @@ public class WifiManager { public boolean startScan(WorkSource workSource) { try { String packageName = mContext.getOpPackageName(); - String featureId = mContext.getFeatureId(); - return mService.startScan(packageName, featureId); + String attributionTag = mContext.getAttributionTag(); + return mService.startScan(packageName, attributionTag); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2713,7 +2714,7 @@ public class WifiManager { public WifiInfo getConnectionInfo() { try { return mService.getConnectionInfo(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2728,35 +2729,38 @@ public class WifiManager { public List<ScanResult> getScanResults() { try { return mService.getScanResults(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Return the filtered ScanResults which may be authenticated by the suggested network - * configurations. - * @param networkSuggestions The list of {@link WifiNetworkSuggestion} - * @param scanResults The scan results to be filtered, this is optional, if it is null or - * empty, wifi system would use the recent scan results in the system. - * @return The map of {@link WifiNetworkSuggestion} and the list of {@link ScanResult} which - * may be authenticated by the corresponding network configuration. + * Get the filtered ScanResults which match the network configurations specified by the + * {@code networkSuggestionsToMatch}. Suggestions which use {@link WifiConfiguration} use + * SSID and the security type to match. Suggestions which use {@link PasspointConfigration} + * use the matching rules of Hotspot 2.0. + * @param networkSuggestionsToMatch The list of {@link WifiNetworkSuggestion} to match against. + * These may or may not be suggestions which are installed on the device. + * @param scanResults The scan results to be filtered. Optional - if not provided(empty list), + * the Wi-Fi service will use the most recent scan results which the system has. + * @return The map of {@link WifiNetworkSuggestion} to the list of {@link ScanResult} + * corresponding to networks which match them. * @hide */ @SystemApi @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE}) @NonNull public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults( - @NonNull List<WifiNetworkSuggestion> networkSuggestions, + @NonNull List<WifiNetworkSuggestion> networkSuggestionsToMatch, @Nullable List<ScanResult> scanResults) { - if (networkSuggestions == null) { + if (networkSuggestionsToMatch == null) { throw new IllegalArgumentException("networkSuggestions must not be null."); } try { return mService.getMatchingScanResults( - networkSuggestions, scanResults, - mContext.getOpPackageName(), mContext.getFeatureId()); + networkSuggestionsToMatch, scanResults, + mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3195,7 +3199,7 @@ public class WifiManager { new LocalOnlyHotspotCallbackProxy(this, executor, callback); try { String packageName = mContext.getOpPackageName(); - String featureId = mContext.getFeatureId(); + String featureId = mContext.getAttributionTag(); int returnCode = mService.startLocalOnlyHotspot(proxy, packageName, featureId, config); if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) { @@ -3372,7 +3376,7 @@ public class WifiManager { */ @NonNull @SystemApi - @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public SoftApConfiguration getSoftApConfiguration() { try { return mService.getSoftApConfiguration(); @@ -3406,9 +3410,10 @@ public class WifiManager { * If the API is called while the tethered soft AP is enabled, the configuration will apply to * the current soft AP if the new configuration only includes * {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)} - * or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(int)} - * or {@link SoftApConfiguration.Builder#enableClientControlByUser(boolean)} - * or {@link SoftApConfiguration.Builder#setClientList(List, List)}. + * or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)} + * or {@link SoftApConfiguration.Builder#setClientControlByUserEnabled(boolean)} + * or {@link SoftApConfiguration.Builder#setBlockedClientList(List)} + * or {@link SoftApConfiguration.Builder#setAllowedClientList(List)} * * Otherwise, the configuration changes will be applied when the Soft AP is next started * (the framework will not stop/start the AP). @@ -5886,7 +5891,7 @@ public class WifiManager { try { mService.registerSuggestionConnectionStatusListener(new Binder(), new SuggestionConnectionStatusListenerProxy(executor, listener), - listener.hashCode(), mContext.getOpPackageName(), mContext.getFeatureId()); + listener.hashCode(), mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 5e4891950466..d299cdc6cad8 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -182,7 +182,7 @@ public class WifiScanner { public List<Integer> getAvailableChannels(int band) { try { Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); List<Integer> channels = bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA); return channels == null ? new ArrayList<>() : channels; } catch (RemoteException e) { @@ -963,7 +963,7 @@ public class WifiScanner { scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); - scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams); } @@ -984,7 +984,7 @@ public class WifiScanner { validateChannel(); Bundle scanParams = new Bundle(); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); - scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key, scanParams); } @@ -1001,7 +1001,7 @@ public class WifiScanner { validateChannel(); Bundle scanParams = new Bundle(); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); - scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0, 0, scanParams); return reply.what == CMD_OP_SUCCEEDED; @@ -1056,7 +1056,7 @@ public class WifiScanner { scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); - scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams); } @@ -1073,7 +1073,7 @@ public class WifiScanner { validateChannel(); Bundle scanParams = new Bundle(); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); - scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key, scanParams); } @@ -1086,7 +1086,7 @@ public class WifiScanner { validateChannel(); Bundle scanParams = new Bundle(); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); - scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0, 0, scanParams); if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) { diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 2ebaa1805b2b..c2ae17c4bdeb 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -267,7 +267,7 @@ public class WifiAwareManager { try { Binder binder = new Binder(); - mService.connect(binder, mContext.getOpPackageName(), mContext.getFeatureId(), + mService.connect(binder, mContext.getOpPackageName(), mContext.getAttributionTag(), new WifiAwareEventCallbackProxy(this, looper, binder, attachCallback, identityChangedListener), configRequest, identityChangedListener != null); @@ -298,7 +298,7 @@ public class WifiAwareManager { } try { - mService.publish(mContext.getOpPackageName(), mContext.getFeatureId(), clientId, + mService.publish(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId, publishConfig, new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback, clientId)); @@ -336,7 +336,7 @@ public class WifiAwareManager { } try { - mService.subscribe(mContext.getOpPackageName(), mContext.getFeatureId(), clientId, + mService.subscribe(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId, subscribeConfig, new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback, clientId)); diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index a310ff6404e7..724ccf0d7c45 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -1180,7 +1180,7 @@ public class WifiP2pManager { == AsyncChannel.STATUS_SUCCESSFUL) { Bundle bundle = new Bundle(); bundle.putString(CALLING_PACKAGE, c.mContext.getOpPackageName()); - bundle.putString(CALLING_FEATURE_ID, c.mContext.getFeatureId()); + bundle.putString(CALLING_FEATURE_ID, c.mContext.getAttributionTag()); bundle.putBinder(CALLING_BINDER, binder); c.mAsyncChannel.sendMessage(UPDATE_CHANNEL_INFO, 0, c.putListener(null), bundle); diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java index cb0c5d41df90..865702af695c 100644 --- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java +++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java @@ -146,8 +146,8 @@ public class WifiRttManager { Binder binder = new Binder(); try { - mService.startRanging(binder, mContext.getOpPackageName(), mContext.getFeatureId(), - workSource, request, new IRttCallback.Stub() { + mService.startRanging(binder, mContext.getOpPackageName(), + mContext.getAttributionTag(), workSource, request, new IRttCallback.Stub() { @Override public void onRangingFailure(int status) throws RemoteException { clearCallingIdentity(); diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml index 987fee79fefd..34e2e3af9cda 100644 --- a/wifi/tests/AndroidTest.xml +++ b/wifi/tests/AndroidTest.xml @@ -25,4 +25,10 @@ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="hidden-api-checks" value="false"/> </test> + + <!-- Only run FrameworksWifiApiTests in MTS if the Wifi Mainline module is installed. --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> + <option name="mainline-module-package-name" value="com.google.android.wifi" /> + </object> </configuration> diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java index 060ddf05b8d7..1a4427034756 100644 --- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -127,8 +127,9 @@ public class SoftApConfigurationTest { .setMaxNumberOfClients(10) .setAutoShutdownEnabled(true) .setShutdownTimeoutMillis(500000) - .enableClientControlByUser(true) - .setClientList(testBlockedClientList, testAllowedClientList) + .setClientControlByUserEnabled(true) + .setBlockedClientList(testBlockedClientList) + .setAllowedClientList(testAllowedClientList) .build(); assertThat(original.getPassphrase()).isEqualTo("secretsecret"); assertThat(original.getSecurityType()).isEqualTo( @@ -264,7 +265,9 @@ public class SoftApConfigurationTest { ArrayList<MacAddress> testBlockedClientList = new ArrayList<>(); testBlockedClientList.add(testMacAddress_1); SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); - configBuilder.setClientList(testBlockedClientList, testAllowedClientList); + configBuilder.setBlockedClientList(testBlockedClientList) + .setAllowedClientList(testAllowedClientList) + .build(); } @Test diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java index 0cc76b68a15e..4881200aa2b7 100644 --- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java @@ -267,7 +267,7 @@ public class WifiScannerTest { assertNull(messageBundle.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY)); assertEquals(mContext.getOpPackageName(), messageBundle.getParcelable(WifiScanner.REQUEST_PACKAGE_NAME_KEY)); - assertEquals(mContext.getFeatureId(), + assertEquals(mContext.getAttributionTag(), messageBundle.getParcelable(WifiScanner.REQUEST_FEATURE_ID_KEY)); } @@ -297,7 +297,7 @@ public class WifiScannerTest { Bundle messageBundle = (Bundle) message.obj; assertEquals(mContext.getOpPackageName(), messageBundle.getParcelable(WifiScanner.REQUEST_PACKAGE_NAME_KEY)); - assertEquals(mContext.getFeatureId(), + assertEquals(mContext.getAttributionTag(), messageBundle.getParcelable(WifiScanner.REQUEST_FEATURE_ID_KEY)); } diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java index a9dcde07fd72..e6eae416ba78 100644 --- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java +++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java @@ -71,7 +71,7 @@ public class WifiRttManagerTest { mMockLooperExecutor = mMockLooper.getNewExecutor(); when(mockContext.getOpPackageName()).thenReturn(packageName); - when(mockContext.getFeatureId()).thenReturn(featureId); + when(mockContext.getAttributionTag()).thenReturn(featureId); } /** |