diff options
173 files changed, 4688 insertions, 2783 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING index cdcd65985ec0..e2e118074aac 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING +++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING @@ -1,6 +1,13 @@ { "presubmit": [ { + "name": "CtsJobSchedulerTestCases", + "options": [ + {"exclude-annotation": "androidx.test.filters.FlakyTest"}, + {"exclude-annotation": "androidx.test.filters.LargeTest"} + ] + }, + { "name": "FrameworksMockingServicesTests", "options": [ {"include-filter": "com.android.server.job"}, diff --git a/apex/statsd/aidl/android/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl index 4a259f50d2f6..b59a97e25bd0 100644 --- a/apex/statsd/aidl/android/os/IStatsManagerService.aidl +++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl @@ -128,7 +128,7 @@ interface IStatsManagerService { void removeConfiguration(in long configId, in String packageName); /** Tell StatsManagerService to register a puller for the given atom tag with statsd. */ - oneway void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, + oneway void registerPullAtomCallback(int atomTag, long coolDownMillis, long timeoutMillis, in int[] additiveFields, IPullAtomCallback pullerCallback); /** Tell StatsManagerService to unregister the pulller for the given atom tag from statsd. */ diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl index 10b1e5b26d12..c8aec5337f6e 100644 --- a/apex/statsd/aidl/android/os/IStatsd.aidl +++ b/apex/statsd/aidl/android/os/IStatsd.aidl @@ -186,8 +186,9 @@ interface IStatsd { * Registers a puller callback function that, when invoked, pulls the data * for the specified atom tag. */ - oneway void registerPullAtomCallback(int uid, int atomTag, long coolDownNs, long timeoutNs, - in int[] additiveFields, IPullAtomCallback pullerCallback); + oneway void registerPullAtomCallback(int uid, int atomTag, long coolDownMillis, + long timeoutMillis,in int[] additiveFields, + IPullAtomCallback pullerCallback); /** * Registers a puller callback function that, when invoked, pulls the data diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java index e637187f23be..f021dcf3cd2f 100644 --- a/apex/statsd/framework/java/android/app/StatsManager.java +++ b/apex/statsd/framework/java/android/app/StatsManager.java @@ -119,14 +119,12 @@ public final class StatsManager { /** * @hide **/ - @VisibleForTesting - public static final long DEFAULT_COOL_DOWN_NS = 1_000_000_000L; // 1 second. + @VisibleForTesting public static final long DEFAULT_COOL_DOWN_MILLIS = 1_000L; // 1 second. /** * @hide **/ - @VisibleForTesting - public static final long DEFAULT_TIMEOUT_NS = 10_000_000_000L; // 10 seconds. + @VisibleForTesting public static final long DEFAULT_TIMEOUT_MILLIS = 10_000L; // 10 seconds. /** * Constructor for StatsManagerClient. @@ -489,23 +487,24 @@ public final class StatsManager { } /** - * Registers a callback for an atom when that atom is to be pulled. The stats service will + * Sets a callback for an atom when that atom is to be pulled. The stats service will * invoke pullData in the callback when the stats service determines that this atom needs to be * pulled. This method should not be called by third-party apps. * * @param atomTag The tag of the atom for this puller callback. * @param metadata Optional metadata specifying the timeout, cool down time, and * additive fields for mapping isolated to host uids. - * @param callback The callback to be invoked when the stats service pulls the atom. * @param executor The executor in which to run the callback. + * @param callback The callback to be invoked when the stats service pulls the atom. * */ @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) - public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata, + public void setPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata, @NonNull @CallbackExecutor Executor executor, @NonNull StatsPullAtomCallback callback) { - long coolDownNs = metadata == null ? DEFAULT_COOL_DOWN_NS : metadata.mCoolDownNs; - long timeoutNs = metadata == null ? DEFAULT_TIMEOUT_NS : metadata.mTimeoutNs; + long coolDownMillis = + metadata == null ? DEFAULT_COOL_DOWN_MILLIS : metadata.mCoolDownMillis; + long timeoutMillis = metadata == null ? DEFAULT_TIMEOUT_MILLIS : metadata.mTimeoutMillis; int[] additiveFields = metadata == null ? new int[0] : metadata.mAdditiveFields; if (additiveFields == null) { additiveFields = new int[0]; @@ -516,8 +515,8 @@ public final class StatsManager { IStatsManagerService service = getIStatsManagerServiceLocked(); PullAtomCallbackInternal rec = new PullAtomCallbackInternal(atomTag, callback, executor); - service.registerPullAtomCallback(atomTag, coolDownNs, timeoutNs, additiveFields, - rec); + service.registerPullAtomCallback( + atomTag, coolDownMillis, timeoutMillis, additiveFields, rec); } catch (RemoteException e) { throw new RuntimeException("Unable to register pull callback", e); } @@ -525,14 +524,14 @@ public final class StatsManager { } /** - * Unregisters a callback for an atom when that atom is to be pulled. Note that any ongoing + * Clears a callback for an atom when that atom is to be pulled. Note that any ongoing * pulls will still occur. This method should not be called by third-party apps. * * @param atomTag The tag of the atom of which to unregister * */ @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) - public void unregisterPullAtomCallback(int atomTag) { + public void clearPullAtomCallback(int atomTag) { synchronized (sLock) { try { IStatsManagerService service = getIStatsManagerServiceLocked(); @@ -585,14 +584,14 @@ public final class StatsManager { * */ public static class PullAtomMetadata { - private final long mCoolDownNs; - private final long mTimeoutNs; + private final long mCoolDownMillis; + private final long mTimeoutMillis; private final int[] mAdditiveFields; // Private Constructor for builder - private PullAtomMetadata(long coolDownNs, long timeoutNs, int[] additiveFields) { - mCoolDownNs = coolDownNs; - mTimeoutNs = timeoutNs; + private PullAtomMetadata(long coolDownMillis, long timeoutMillis, int[] additiveFields) { + mCoolDownMillis = coolDownMillis; + mTimeoutMillis = timeoutMillis; mAdditiveFields = additiveFields; } @@ -600,8 +599,8 @@ public final class StatsManager { * Builder for PullAtomMetadata. */ public static class Builder { - private long mCoolDownNs; - private long mTimeoutNs; + private long mCoolDownMillis; + private long mTimeoutMillis; private int[] mAdditiveFields; /** @@ -609,27 +608,28 @@ public final class StatsManager { * StatsManager#registerPullAtomCallback */ public Builder() { - mCoolDownNs = DEFAULT_COOL_DOWN_NS; - mTimeoutNs = DEFAULT_TIMEOUT_NS; + mCoolDownMillis = DEFAULT_COOL_DOWN_MILLIS; + mTimeoutMillis = DEFAULT_TIMEOUT_MILLIS; mAdditiveFields = null; } /** - * Set the cool down time of the pull in nanoseconds. If two successive pulls are issued - * within the cool down, a cached version of the first will be used for the second. + * Set the cool down time of the pull in milliseconds. If two successive pulls are + * issued within the cool down, a cached version of the first pull will be used for the + * second pull. */ @NonNull - public Builder setCoolDownNs(long coolDownNs) { - mCoolDownNs = coolDownNs; + public Builder setCoolDownMillis(long coolDownMillis) { + mCoolDownMillis = coolDownMillis; return this; } /** - * Set the maximum time the pull can take in nanoseconds. + * Set the maximum time the pull can take in milliseconds. */ @NonNull - public Builder setTimeoutNs(long timeoutNs) { - mTimeoutNs = timeoutNs; + public Builder setTimeoutMillis(long timeoutMillis) { + mTimeoutMillis = timeoutMillis; return this; } @@ -652,30 +652,32 @@ public final class StatsManager { */ @NonNull public PullAtomMetadata build() { - return new PullAtomMetadata(mCoolDownNs, mTimeoutNs, mAdditiveFields); + return new PullAtomMetadata(mCoolDownMillis, mTimeoutMillis, mAdditiveFields); } } /** - * @hide + * Return the cool down time of this pull in milliseconds. */ - @VisibleForTesting - public long getCoolDownNs() { - return mCoolDownNs; + public long getCoolDownMillis() { + return mCoolDownMillis; } /** - * @hide + * Return the maximum amount of time this pull can take in milliseconds. */ - @VisibleForTesting - public long getTimeoutNs() { - return mTimeoutNs; + public long getTimeoutMillis() { + return mTimeoutMillis; } /** - * @hide + * Return the additive fields of this pulled atom. + * + * This is only applicable for atoms that have a uid field. When tasks are run in + * isolated processes, the data will be attributed to the host uid. Additive fields + * will be combined when the non-additive fields are the same. */ - @VisibleForTesting + @Nullable public int[] getAdditiveFields() { return mAdditiveFields; } diff --git a/apex/statsd/framework/test/src/android/app/PullAtomMetadataTest.java b/apex/statsd/framework/test/src/android/app/PullAtomMetadataTest.java index 0ae613400b18..fd386bd8e32e 100644 --- a/apex/statsd/framework/test/src/android/app/PullAtomMetadataTest.java +++ b/apex/statsd/framework/test/src/android/app/PullAtomMetadataTest.java @@ -33,28 +33,28 @@ public final class PullAtomMetadataTest { @Test public void testEmpty() { PullAtomMetadata metadata = new PullAtomMetadata.Builder().build(); - assertThat(metadata.getTimeoutNs()).isEqualTo(StatsManager.DEFAULT_TIMEOUT_NS); - assertThat(metadata.getCoolDownNs()).isEqualTo(StatsManager.DEFAULT_COOL_DOWN_NS); + assertThat(metadata.getTimeoutMillis()).isEqualTo(StatsManager.DEFAULT_TIMEOUT_MILLIS); + assertThat(metadata.getCoolDownMillis()).isEqualTo(StatsManager.DEFAULT_COOL_DOWN_MILLIS); assertThat(metadata.getAdditiveFields()).isNull(); } @Test - public void testSetTimeoutNs() { - long timeoutNs = 500_000_000L; + public void testSetTimeoutMillis() { + long timeoutMillis = 500L; PullAtomMetadata metadata = - new PullAtomMetadata.Builder().setTimeoutNs(timeoutNs).build(); - assertThat(metadata.getTimeoutNs()).isEqualTo(timeoutNs); - assertThat(metadata.getCoolDownNs()).isEqualTo(StatsManager.DEFAULT_COOL_DOWN_NS); + new PullAtomMetadata.Builder().setTimeoutMillis(timeoutMillis).build(); + assertThat(metadata.getTimeoutMillis()).isEqualTo(timeoutMillis); + assertThat(metadata.getCoolDownMillis()).isEqualTo(StatsManager.DEFAULT_COOL_DOWN_MILLIS); assertThat(metadata.getAdditiveFields()).isNull(); } @Test - public void testSetCoolDownNs() { - long coolDownNs = 10_000_000_000L; + public void testSetCoolDownMillis() { + long coolDownMillis = 10_000L; PullAtomMetadata metadata = - new PullAtomMetadata.Builder().setCoolDownNs(coolDownNs).build(); - assertThat(metadata.getTimeoutNs()).isEqualTo(StatsManager.DEFAULT_TIMEOUT_NS); - assertThat(metadata.getCoolDownNs()).isEqualTo(coolDownNs); + new PullAtomMetadata.Builder().setCoolDownMillis(coolDownMillis).build(); + assertThat(metadata.getTimeoutMillis()).isEqualTo(StatsManager.DEFAULT_TIMEOUT_MILLIS); + assertThat(metadata.getCoolDownMillis()).isEqualTo(coolDownMillis); assertThat(metadata.getAdditiveFields()).isNull(); } @@ -63,23 +63,23 @@ public final class PullAtomMetadataTest { int[] fields = {2, 4, 6}; PullAtomMetadata metadata = new PullAtomMetadata.Builder().setAdditiveFields(fields).build(); - assertThat(metadata.getTimeoutNs()).isEqualTo(StatsManager.DEFAULT_TIMEOUT_NS); - assertThat(metadata.getCoolDownNs()).isEqualTo(StatsManager.DEFAULT_COOL_DOWN_NS); + assertThat(metadata.getTimeoutMillis()).isEqualTo(StatsManager.DEFAULT_TIMEOUT_MILLIS); + assertThat(metadata.getCoolDownMillis()).isEqualTo(StatsManager.DEFAULT_COOL_DOWN_MILLIS); assertThat(metadata.getAdditiveFields()).isEqualTo(fields); } @Test public void testSetAllElements() { - long timeoutNs = 300L; - long coolDownNs = 9572L; + long timeoutMillis = 300L; + long coolDownMillis = 9572L; int[] fields = {3, 2}; PullAtomMetadata metadata = new PullAtomMetadata.Builder() - .setTimeoutNs(timeoutNs) - .setCoolDownNs(coolDownNs) + .setTimeoutMillis(timeoutMillis) + .setCoolDownMillis(coolDownMillis) .setAdditiveFields(fields) .build(); - assertThat(metadata.getTimeoutNs()).isEqualTo(timeoutNs); - assertThat(metadata.getCoolDownNs()).isEqualTo(coolDownNs); + assertThat(metadata.getTimeoutMillis()).isEqualTo(timeoutMillis); + assertThat(metadata.getCoolDownMillis()).isEqualTo(coolDownMillis); assertThat(metadata.getAdditiveFields()).isEqualTo(fields); } } diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java index 4e4bc40b727f..58c78da5cea7 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java @@ -136,25 +136,25 @@ public class StatsManagerService extends IStatsManagerService.Stub { } private static class PullerValue { - private final long mCoolDownNs; - private final long mTimeoutNs; + private final long mCoolDownMillis; + private final long mTimeoutMillis; private final int[] mAdditiveFields; private final IPullAtomCallback mCallback; - PullerValue(long coolDownNs, long timeoutNs, int[] additiveFields, + PullerValue(long coolDownMillis, long timeoutMillis, int[] additiveFields, IPullAtomCallback callback) { - mCoolDownNs = coolDownNs; - mTimeoutNs = timeoutNs; + mCoolDownMillis = coolDownMillis; + mTimeoutMillis = timeoutMillis; mAdditiveFields = additiveFields; mCallback = callback; } - public long getCoolDownNs() { - return mCoolDownNs; + public long getCoolDownMillis() { + return mCoolDownMillis; } - public long getTimeoutNs() { - return mTimeoutNs; + public long getTimeoutMillis() { + return mTimeoutMillis; } public int[] getAdditiveFields() { @@ -169,12 +169,13 @@ public class StatsManagerService extends IStatsManagerService.Stub { private final ArrayMap<PullerKey, PullerValue> mPullers = new ArrayMap<>(); @Override - public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, + public void registerPullAtomCallback(int atomTag, long coolDownMillis, long timeoutMillis, int[] additiveFields, IPullAtomCallback pullerCallback) { enforceRegisterStatsPullAtomPermission(); int callingUid = Binder.getCallingUid(); PullerKey key = new PullerKey(callingUid, atomTag); - PullerValue val = new PullerValue(coolDownNs, timeoutNs, additiveFields, pullerCallback); + PullerValue val = + new PullerValue(coolDownMillis, timeoutMillis, additiveFields, pullerCallback); // Always cache the puller in StatsManagerService. If statsd is down, we will register the // puller when statsd comes back up. @@ -189,8 +190,8 @@ public class StatsManagerService extends IStatsManagerService.Stub { final long token = Binder.clearCallingIdentity(); try { - statsd.registerPullAtomCallback( - callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback); + statsd.registerPullAtomCallback(callingUid, atomTag, coolDownMillis, timeoutMillis, + additiveFields, pullerCallback); } catch (RemoteException e) { Log.e(TAG, "Failed to access statsd to register puller for atom " + atomTag); } finally { @@ -596,8 +597,8 @@ public class StatsManagerService extends IStatsManagerService.Stub { for (Map.Entry<PullerKey, PullerValue> entry : pullersCopy.entrySet()) { PullerKey key = entry.getKey(); PullerValue value = entry.getValue(); - statsd.registerPullAtomCallback(key.getUid(), key.getAtom(), value.getCoolDownNs(), - value.getTimeoutNs(), value.getAdditiveFields(), value.getCallback()); + statsd.registerPullAtomCallback(key.getUid(), key.getAtom(), value.getCoolDownMillis(), + value.getTimeoutMillis(), value.getAdditiveFields(), value.getCallback()); } } diff --git a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp index eb97f6559b6d..9e5aa952d9bc 100644 --- a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp +++ b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <android/binder_process.h> #include <jni.h> #include <log/log.h> #include <stats_event.h> @@ -32,17 +31,6 @@ static int64_t sLatencyMillis; static int32_t sAtomsPerPull; static int32_t sNumPulls = 0; -static bool initialized = false; - -static void init() { - if (!initialized) { - initialized = true; - // Set up the binder - ABinderProcess_setThreadPoolMaxThreadCount(9); - ABinderProcess_startThreadPool(); - } -} - static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag, AStatsEventList* data, void* /*cookie*/) { sNumPulls++; @@ -62,7 +50,6 @@ Java_com_android_internal_os_statsd_libstats_LibStatsPullTests_registerStatsPull JNIEnv* /*env*/, jobject /* this */, jint atomTag, jlong timeoutNs, jlong coolDownNs, jint pullRetVal, jlong latencyMillis, int atomsPerPull) { - init(); sAtomTag = atomTag; sPullReturnVal = pullRetVal; sLatencyMillis = latencyMillis; diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java index e119b4c47604..d4e51e040d1c 100644 --- a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java +++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java @@ -229,7 +229,7 @@ public class LibStatsPullTests { // Let the current bucket finish. Thread.sleep(LONG_SLEEP_MILLIS); List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); - statsManager.unregisterPullAtomCallback(PULL_ATOM_TAG); + unregisterStatsPuller(PULL_ATOM_TAG); assertThat(data.size()).isEqualTo(sAtomsPerPull); for (int i = 0; i < data.size(); i++) { diff --git a/api/current.txt b/api/current.txt index 563665fb519a..d4cbe6d93486 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9924,8 +9924,11 @@ package android.content { field public static final String EXTRA_REFRESH_SUPPORTED = "android.content.extra.REFRESH_SUPPORTED"; field public static final String EXTRA_SIZE = "android.content.extra.SIZE"; field public static final String EXTRA_TOTAL_COUNT = "android.content.extra.TOTAL_COUNT"; + field public static final int NOTIFY_DELETE = 16; // 0x10 + field public static final int NOTIFY_INSERT = 4; // 0x4 field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2 field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1 + field public static final int NOTIFY_UPDATE = 8; // 0x8 field public static final String QUERY_ARG_GROUP_COLUMNS = "android:query-arg-group-columns"; field public static final String QUERY_ARG_LIMIT = "android:query-arg-limit"; field public static final String QUERY_ARG_OFFSET = "android:query-arg-offset"; @@ -12967,9 +12970,13 @@ package android.database { ctor public ContentObserver(android.os.Handler); method public boolean deliverSelfNotifications(); method @Deprecated public final void dispatchChange(boolean); - method public final void dispatchChange(boolean, android.net.Uri); + method public final void dispatchChange(boolean, @Nullable android.net.Uri); + method public final void dispatchChange(boolean, @Nullable android.net.Uri, int); + method public final void dispatchChange(boolean, @NonNull Iterable<android.net.Uri>, int); method public void onChange(boolean); - method public void onChange(boolean, android.net.Uri); + method public void onChange(boolean, @Nullable android.net.Uri); + method public void onChange(boolean, @Nullable android.net.Uri, int); + method public void onChange(boolean, @NonNull Iterable<android.net.Uri>, int); } public interface CrossProcessCursor extends android.database.Cursor { @@ -26902,7 +26909,6 @@ package android.media { method public final void notifyRequestFailed(long, int); method public final void notifyRoutes(@NonNull java.util.Collection<android.media.MediaRoute2Info>); method public final void notifySessionCreated(@NonNull android.media.RoutingSessionInfo, long); - method public final void notifySessionCreationFailed(long); method public final void notifySessionReleased(@NonNull String); method public final void notifySessionUpdated(@NonNull android.media.RoutingSessionInfo); method @CallSuper @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); @@ -43507,6 +43513,7 @@ package android.service.controls { field public static final int TYPE_RADIATOR = 16; // 0x10 field public static final int TYPE_REFRIGERATOR = 48; // 0x30 field public static final int TYPE_REMOTE_CONTROL = 17; // 0x11 + field public static final int TYPE_ROUTINE = 52; // 0x34 field public static final int TYPE_SECURITY_SYSTEM = 46; // 0x2e field public static final int TYPE_SET_TOP = 18; // 0x12 field public static final int TYPE_SHOWER = 29; // 0x1d @@ -48308,10 +48315,6 @@ package android.telephony { field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID"; field public static final String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER"; field public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU"; - field public static final int MODEM_COUNT_DUAL_MODEM = 2; // 0x2 - field public static final int MODEM_COUNT_NO_MODEM = 0; // 0x0 - field public static final int MODEM_COUNT_SINGLE_MODEM = 1; // 0x1 - field public static final int MODEM_COUNT_TRI_MODEM = 3; // 0x3 field public static final int MULTISIM_ALLOWED = 0; // 0x0 field public static final int MULTISIM_NOT_SUPPORTED_BY_CARRIER = 2; // 0x2 field public static final int MULTISIM_NOT_SUPPORTED_BY_HARDWARE = 1; // 0x1 diff --git a/api/system-current.txt b/api/system-current.txt index c51fab44cbf3..f0621124eeae 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -702,12 +702,12 @@ package android.app { public final class StatsManager { method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void addConfig(long, byte[]) throws android.app.StatsManager.StatsUnavailableException; method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean addConfiguration(long, byte[]); + method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void clearPullAtomCallback(int); method @Deprecated @Nullable @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getData(long); method @Deprecated @Nullable @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getMetadata(); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] getRegisteredExperimentIds() throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException; - method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void registerPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException; method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long); method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException; @@ -715,7 +715,7 @@ package android.app { method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException; - method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void unregisterPullAtomCallback(int); + method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void setPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback); field public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED"; field public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = "android.app.extra.STATS_ACTIVE_CONFIG_KEYS"; field public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES"; @@ -729,14 +729,17 @@ package android.app { } public static class StatsManager.PullAtomMetadata { + method @Nullable public int[] getAdditiveFields(); + method public long getCoolDownMillis(); + method public long getTimeoutMillis(); } public static class StatsManager.PullAtomMetadata.Builder { ctor public StatsManager.PullAtomMetadata.Builder(); method @NonNull public android.app.StatsManager.PullAtomMetadata build(); method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setAdditiveFields(@NonNull int[]); - method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setCoolDownNs(long); - method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setTimeoutNs(long); + method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setCoolDownMillis(long); + method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setTimeoutMillis(long); } public static interface StatsManager.StatsPullAtomCallback { @@ -4992,6 +4995,11 @@ package android.media.tv.tuner.filter { field public static final int LENGTH_TYPE_UNDEFINED = 0; // 0x0 field public static final int LENGTH_TYPE_WITHOUT_ADDITIONAL_HEADER = 1; // 0x1 field public static final int LENGTH_TYPE_WITH_ADDITIONAL_HEADER = 2; // 0x2 + field public static final int PACKET_TYPE_COMPRESSED = 2; // 0x2 + field public static final int PACKET_TYPE_EXTENSION = 6; // 0x6 + field public static final int PACKET_TYPE_IPV4 = 0; // 0x0 + field public static final int PACKET_TYPE_MPEG2_TS = 7; // 0x7 + field public static final int PACKET_TYPE_SIGNALING = 4; // 0x4 } public static class AlpFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.AlpFilterConfiguration.Builder> { @@ -5072,6 +5080,7 @@ package android.media.tv.tuner.filter { field public static final int TYPE_MMTP = 2; // 0x2 field public static final int TYPE_TLV = 8; // 0x8 field public static final int TYPE_TS = 1; // 0x1 + field public static final int TYPE_UNDEFINED = 0; // 0x0 } public interface FilterCallback { @@ -5082,9 +5091,6 @@ package android.media.tv.tuner.filter { public abstract class FilterConfiguration { method @Nullable public android.media.tv.tuner.filter.Settings getSettings(); method public abstract int getType(); - field public static final int PACKET_TYPE_COMPRESSED = 2; // 0x2 - field public static final int PACKET_TYPE_IPV4 = 0; // 0x0 - field public static final int PACKET_TYPE_SIGNALING = 4; // 0x4 } public abstract static class FilterConfiguration.Builder<T extends android.media.tv.tuner.filter.FilterConfiguration.Builder<T>> { @@ -5282,6 +5288,11 @@ package android.media.tv.tuner.filter { method public int getType(); method public boolean isCompressedIpPacket(); method public boolean isPassthrough(); + field public static final int PACKET_TYPE_COMPRESSED = 3; // 0x3 + field public static final int PACKET_TYPE_IPV4 = 1; // 0x1 + field public static final int PACKET_TYPE_IPV6 = 2; // 0x2 + field public static final int PACKET_TYPE_NULL = 255; // 0xff + field public static final int PACKET_TYPE_SIGNALING = 254; // 0xfe } public static class TlvFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.TlvFilterConfiguration.Builder> { @@ -11658,7 +11669,6 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn(); 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 public boolean isCurrentSimOperator(@NonNull String, int, @Nullable String); 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 public boolean isDataConnectivityPossible(); @@ -11677,6 +11687,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired(); 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(); diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 0c3a49a9d340..bd6ca47cb4b8 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -122,7 +122,6 @@ cc_defaults { "libbinder_ndk", "libincident", "liblog", - "libstatssocket", "statsd-aidl-ndk_platform", ], } @@ -165,10 +164,6 @@ cc_library_static { export_generated_headers: [ "atoms_info.h", ], - shared_libs: [ - "libcutils", - "libstatslog", - ], apex_available: [ //TODO(b/149782403): Remove this once statsd no longer links against libstatsmetadata "com.android.os.statsd", @@ -215,7 +210,10 @@ cc_binary { type: "lite", }, - shared_libs: ["libgtest_prod"], + shared_libs: [ + "libgtest_prod", + "libstatssocket", + ], apex_available: [ "com.android.os.statsd", diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 7ab6c7111b3e..f18aaa5a9ea0 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1196,13 +1196,14 @@ Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) { return Status::ok(); } -Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs, - int64_t timeoutNs, const std::vector<int32_t>& additiveFields, - const shared_ptr<IPullAtomCallback>& pullerCallback) { +Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownMillis, + int64_t timeoutMillis, + const std::vector<int32_t>& additiveFields, + const shared_ptr<IPullAtomCallback>& pullerCallback) { ENFORCE_UID(AID_SYSTEM); - VLOG("StatsService::registerPullAtomCallback called."); - mPullerManager->RegisterPullAtomCallback(uid, atomTag, coolDownNs, timeoutNs, additiveFields, + mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis), + MillisToNano(timeoutMillis), additiveFields, pullerCallback); return Status::ok(); } diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index e6723c83e02a..c256e1f2ae9c 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -167,8 +167,9 @@ public: /** * Binder call to register a callback function for a pulled atom. */ - virtual Status registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs, - int64_t timeoutNs, const std::vector<int32_t>& additiveFields, + virtual Status registerPullAtomCallback( + int32_t uid, int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis, + const std::vector<int32_t>& additiveFields, const shared_ptr<IPullAtomCallback>& pullerCallback) override; /** diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto index 9c875ba7502d..40a24dc52e52 100644 --- a/cmds/statsd/src/atom_field_options.proto +++ b/cmds/statsd/src/atom_field_options.proto @@ -116,5 +116,5 @@ extend google.protobuf.FieldOptions { optional bool allow_from_any_uid = 50003 [default = false]; - optional string module = 50004; + repeated string module = 50004; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index e58e7bc11315..9645a466ec16 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3600,9 +3600,10 @@ message Notification { // See NotificationSectionsManager.PriorityBucket. enum NotificationSection { SECTION_UNKNOWN = 0; - SECTION_PEOPLE = 1; - SECTION_ALERTING = 2; - SECTION_SILENT = 3; + SECTION_HEADS_UP = 1; + SECTION_PEOPLE = 2; + SECTION_ALERTING = 3; + SECTION_SILENT = 4; } optional NotificationSection section = 6; } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index c7f42cb85943..ae786aa30ae0 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -629,7 +629,10 @@ public abstract class ContentResolver implements ContentInterface { /** @hide */ @IntDef(flag = true, prefix = { "NOTIFY_" }, value = { NOTIFY_SYNC_TO_NETWORK, - NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS + NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS, + NOTIFY_INSERT, + NOTIFY_UPDATE, + NOTIFY_DELETE }) @Retention(RetentionPolicy.SOURCE) public @interface NotifyFlags {} @@ -651,6 +654,36 @@ public abstract class ContentResolver implements ContentInterface { public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 1<<1; /** + * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: typically set + * by a {@link ContentProvider} to indicate that this notification is the + * result of an {@link ContentProvider#insert} call. + * <p> + * Sending these detailed flags are optional, but providers are strongly + * recommended to send them. + */ + public static final int NOTIFY_INSERT = 1 << 2; + + /** + * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: typically set + * by a {@link ContentProvider} to indicate that this notification is the + * result of an {@link ContentProvider#update} call. + * <p> + * Sending these detailed flags are optional, but providers are strongly + * recommended to send them. + */ + public static final int NOTIFY_UPDATE = 1 << 3; + + /** + * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: typically set + * by a {@link ContentProvider} to indicate that this notification is the + * result of a {@link ContentProvider#delete} call. + * <p> + * Sending these detailed flags are optional, but providers are strongly + * recommended to send them. + */ + public static final int NOTIFY_DELETE = 1 << 4; + + /** * No exception, throttled by app standby normally. * @hide */ diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java index 69ca581e1559..ede264d042ce 100644 --- a/core/java/android/database/ContentObserver.java +++ b/core/java/android/database/ContentObserver.java @@ -16,11 +16,17 @@ package android.database; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ContentResolver.NotifyFlags; import android.net.Uri; import android.os.Handler; import android.os.UserHandle; +import java.util.Arrays; + /** * Receives call backs for changes to content. * Must be implemented by objects which are added to a {@link ContentObservable}. @@ -101,12 +107,10 @@ public abstract class ContentObserver { * This method is called when a content change occurs. * Includes the changed content Uri when available. * <p> - * Subclasses should override this method to handle content changes. - * To ensure correct operation on older versions of the framework that - * did not provide a Uri argument, applications should also implement - * the {@link #onChange(boolean)} overload of this method whenever they - * implement the {@link #onChange(boolean, Uri)} overload. - * </p><p> + * Subclasses should override this method to handle content changes. To + * ensure correct operation on older versions of the framework that did not + * provide richer arguments, applications should implement all overloads. + * <p> * Example implementation: * <pre><code> * // Implement the onChange(boolean) method to delegate the change notification to @@ -126,38 +130,63 @@ public abstract class ContentObserver { * </p> * * @param selfChange True if this is a self-change notification. - * @param uri The Uri of the changed content, or null if unknown. + * @param uri The Uri of the changed content. */ - public void onChange(boolean selfChange, Uri uri) { + public void onChange(boolean selfChange, @Nullable Uri uri) { onChange(selfChange); } /** - * Dispatches a change notification to the observer. Includes the changed - * content Uri when available and also the user whose content changed. + * This method is called when a content change occurs. Includes the changed + * content Uri when available. + * <p> + * Subclasses should override this method to handle content changes. To + * ensure correct operation on older versions of the framework that did not + * provide richer arguments, applications should implement all overloads. * * @param selfChange True if this is a self-change notification. - * @param uri The Uri of the changed content, or null if unknown. - * @param userId The user whose content changed. Can be either a specific - * user or {@link UserHandle#USER_ALL}. - * - * @hide + * @param uri The Uri of the changed content. + * @param flags Flags indicating details about this change. */ - public void onChange(boolean selfChange, Uri uri, int userId) { + public void onChange(boolean selfChange, @Nullable Uri uri, @NotifyFlags int flags) { onChange(selfChange, uri); } /** - * Dispatches a change notification to the observer. + * This method is called when a content change occurs. Includes the changed + * content Uris when available. * <p> - * If a {@link Handler} was supplied to the {@link ContentObserver} constructor, - * then a call to the {@link #onChange} method is posted to the handler's message queue. - * Otherwise, the {@link #onChange} method is invoked immediately on this thread. - * </p> + * Subclasses should override this method to handle content changes. To + * ensure correct operation on older versions of the framework that did not + * provide richer arguments, applications should implement all overloads. * * @param selfChange True if this is a self-change notification. + * @param uris The Uris of the changed content. + * @param flags Flags indicating details about this change. + */ + public void onChange(boolean selfChange, @NonNull Iterable<Uri> uris, @NotifyFlags int flags) { + for (Uri uri : uris) { + onChange(selfChange, uri, flags); + } + } + + /** @hide */ + public void onChange(boolean selfChange, @NonNull Iterable<Uri> uris, @NotifyFlags int flags, + @UserIdInt int userId) { + onChange(selfChange, uris, flags); + } + + /** + * Dispatches a change notification to the observer. + * <p> + * If a {@link Handler} was supplied to the {@link ContentObserver} + * constructor, then a call to the {@link #onChange} method is posted to the + * handler's message queue. Otherwise, the {@link #onChange} method is + * invoked immediately on this thread. * - * @deprecated Use {@link #dispatchChange(boolean, Uri)} instead. + * @deprecated Callers should migrate towards using a richer overload that + * provides more details about the change, such as + * {@link #dispatchChange(boolean, Iterable, int)}. */ @Deprecated public final void dispatchChange(boolean selfChange) { @@ -165,57 +194,66 @@ public abstract class ContentObserver { } /** - * Dispatches a change notification to the observer. - * Includes the changed content Uri when available. + * Dispatches a change notification to the observer. Includes the changed + * content Uri when available. * <p> - * If a {@link Handler} was supplied to the {@link ContentObserver} constructor, - * then a call to the {@link #onChange} method is posted to the handler's message queue. - * Otherwise, the {@link #onChange} method is invoked immediately on this thread. - * </p> + * If a {@link Handler} was supplied to the {@link ContentObserver} + * constructor, then a call to the {@link #onChange} method is posted to the + * handler's message queue. Otherwise, the {@link #onChange} method is + * invoked immediately on this thread. * * @param selfChange True if this is a self-change notification. - * @param uri The Uri of the changed content, or null if unknown. + * @param uri The Uri of the changed content. */ - public final void dispatchChange(boolean selfChange, Uri uri) { - dispatchChange(selfChange, uri, UserHandle.getCallingUserId()); + public final void dispatchChange(boolean selfChange, @Nullable Uri uri) { + dispatchChange(selfChange, Arrays.asList(uri), 0, UserHandle.getCallingUserId()); } /** * Dispatches a change notification to the observer. Includes the changed - * content Uri when available and also the user whose content changed. + * content Uri when available. * <p> - * If a {@link Handler} was supplied to the {@link ContentObserver} constructor, - * then a call to the {@link #onChange} method is posted to the handler's message queue. - * Otherwise, the {@link #onChange} method is invoked immediately on this thread. - * </p> + * If a {@link Handler} was supplied to the {@link ContentObserver} + * constructor, then a call to the {@link #onChange} method is posted to the + * handler's message queue. Otherwise, the {@link #onChange} method is + * invoked immediately on this thread. * * @param selfChange True if this is a self-change notification. - * @param uri The Uri of the changed content, or null if unknown. - * @param userId The user whose content changed. + * @param uri The Uri of the changed content. + * @param flags Flags indicating details about this change. */ - private void dispatchChange(boolean selfChange, Uri uri, int userId) { - if (mHandler == null) { - onChange(selfChange, uri, userId); - } else { - mHandler.post(new NotificationRunnable(selfChange, uri, userId)); - } + public final void dispatchChange(boolean selfChange, @Nullable Uri uri, + @NotifyFlags int flags) { + dispatchChange(selfChange, Arrays.asList(uri), flags, UserHandle.getCallingUserId()); } + /** + * Dispatches a change notification to the observer. Includes the changed + * content Uris when available. + * <p> + * If a {@link Handler} was supplied to the {@link ContentObserver} + * constructor, then a call to the {@link #onChange} method is posted to the + * handler's message queue. Otherwise, the {@link #onChange} method is + * invoked immediately on this thread. + * + * @param selfChange True if this is a self-change notification. + * @param uris The Uri of the changed content. + * @param flags Flags indicating details about this change. + */ + public final void dispatchChange(boolean selfChange, @NonNull Iterable<Uri> uris, + @NotifyFlags int flags) { + dispatchChange(selfChange, uris, flags, UserHandle.getCallingUserId()); + } - private final class NotificationRunnable implements Runnable { - private final boolean mSelfChange; - private final Uri mUri; - private final int mUserId; - - public NotificationRunnable(boolean selfChange, Uri uri, int userId) { - mSelfChange = selfChange; - mUri = uri; - mUserId = userId; - } - - @Override - public void run() { - ContentObserver.this.onChange(mSelfChange, mUri, mUserId); + /** @hide */ + public final void dispatchChange(boolean selfChange, @NonNull Iterable<Uri> uris, + @NotifyFlags int flags, @UserIdInt int userId) { + if (mHandler == null) { + onChange(selfChange, uris, flags, userId); + } else { + mHandler.post(() -> { + onChange(selfChange, uris, flags, userId); + }); } } @@ -228,9 +266,16 @@ public abstract class ContentObserver { @Override public void onChange(boolean selfChange, Uri uri, int userId) { + // This is kept intact purely for apps using hidden APIs, to + // redirect to the updated implementation + onChangeEtc(selfChange, new Uri[] { uri }, 0, userId); + } + + @Override + public void onChangeEtc(boolean selfChange, Uri[] uris, int flags, int userId) { ContentObserver contentObserver = mContentObserver; if (contentObserver != null) { - contentObserver.dispatchChange(selfChange, uri, userId); + contentObserver.dispatchChange(selfChange, Arrays.asList(uris), flags, userId); } } diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java index 02eddf239dca..1855dd254ad4 100644 --- a/core/java/android/database/CursorToBulkCursorAdaptor.java +++ b/core/java/android/database/CursorToBulkCursorAdaptor.java @@ -16,9 +16,14 @@ package android.database; +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.content.ContentResolver.NotifyFlags; import android.net.Uri; import android.os.*; +import java.util.ArrayList; + /** * Wraps a BulkCursor around an existing Cursor making it remotable. @@ -76,9 +81,18 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative } @Override - public void onChange(boolean selfChange, Uri uri) { + public void onChange(boolean selfChange, @NonNull Iterable<Uri> uris, + @NotifyFlags int flags, @UserIdInt int userId) { + // Since we deliver changes from the most-specific to least-specific + // overloads, we only need to redirect from the most-specific local + // method to the most-specific remote method + + final ArrayList<Uri> asList = new ArrayList<>(); + uris.forEach(asList::add); + final Uri[] asArray = asList.toArray(new Uri[asList.size()]); + try { - mRemote.onChange(selfChange, uri, android.os.Process.myUid()); + mRemote.onChangeEtc(selfChange, asArray, flags, userId); } catch (RemoteException ex) { // Do nothing, the far side is dead } diff --git a/core/java/android/database/IContentObserver.aidl b/core/java/android/database/IContentObserver.aidl index 623556695341..19284cf40617 100644 --- a/core/java/android/database/IContentObserver.aidl +++ b/core/java/android/database/IContentObserver.aidl @@ -22,8 +22,7 @@ import android.net.Uri; /** * @hide */ -interface IContentObserver -{ +interface IContentObserver { /** * This method is called when an update occurs to the cursor that is being * observed. selfUpdate is true if the update was caused by a call to @@ -31,4 +30,11 @@ interface IContentObserver */ @UnsupportedAppUsage oneway void onChange(boolean selfUpdate, in Uri uri, int userId); + + /** + * This method is called when an update occurs to the cursor that is being + * observed. selfUpdate is true if the update was caused by a call to + * commit on the cursor that is being observed. + */ + oneway void onChangeEtc(boolean selfUpdate, in Uri[] uri, int flags, int userId); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 9e639346de73..1b6c1ee4f7e2 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -611,6 +611,10 @@ public class InputMethodService extends AbstractInputMethodService { public void unbindInput() { if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding + " ic=" + mInputConnection); + // Unbind input is per process per display. + // TODO(b/150902448): free-up IME surface when target is changing. + // e.g. DisplayContent#setInputMethodTarget() + removeImeSurface(); onUnbindInput(); mInputBinding = null; mInputConnection = null; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4216cf387380..83a304facce3 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6608,6 +6608,18 @@ public final class Settings { "accessibility_button_target_component"; /** + * Setting specifying the accessibility services, accessibility shortcut targets, + * or features to be toggled via the long press accessibility button in the navigation bar. + * + * <p> This is a colon-separated string list which contains the flattened + * {@link ComponentName} and the class name of a system class implementing a supported + * accessibility feature. + * @hide + */ + public static final String ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS = + "accessibility_button_long_press_targets"; + + /** * The system class name of magnification controller which is a target to be toggled via * accessibility shortcut or accessibility button. * diff --git a/core/java/android/service/controls/DeviceTypes.java b/core/java/android/service/controls/DeviceTypes.java index 6594d2cf4ba2..ac3b36c72a25 100644 --- a/core/java/android/service/controls/DeviceTypes.java +++ b/core/java/android/service/controls/DeviceTypes.java @@ -27,7 +27,7 @@ import java.lang.annotation.RetentionPolicy; public class DeviceTypes { // Update this when adding new concrete types. Does not count TYPE_UNKNOWN - private static final int NUM_CONCRETE_TYPES = 51; + private static final int NUM_CONCRETE_TYPES = 52; public static final @DeviceType int TYPE_UNKNOWN = 0; public static final @DeviceType int TYPE_AC_HEATER = 1; @@ -87,6 +87,11 @@ public class DeviceTypes { public static final @DeviceType int TYPE_CAMERA = 50; public static final @DeviceType int TYPE_DOORBELL = 51; + /* + * Also known as macros, routines can aggregate a series of actions across multiple devices + */ + public static final @DeviceType int TYPE_ROUTINE = 52; + // Update this when adding new generic types. private static final int NUM_GENERIC_TYPES = 7; public static final @DeviceType int TYPE_GENERIC_ON_OFF = -1; @@ -165,7 +170,8 @@ public class DeviceTypes { TYPE_REFRIGERATOR, TYPE_THERMOSTAT, TYPE_CAMERA, - TYPE_DOORBELL + TYPE_DOORBELL, + TYPE_ROUTINE }) public @interface DeviceType {} diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index cfbe393bfcc8..6b87a10643fd 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -71,7 +71,7 @@ public class FeatureFlagUtils { // introduced in R and will be removed in the next release (b/148367230). DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "false"); - DEFAULT_FLAGS.put("settings_tether_all_in_one", "false"); + DEFAULT_FLAGS.put("settings_tether_all_in_one", "true"); DEFAULT_FLAGS.put(SETTINGS_SCHEDULES_FLAG, "false"); DEFAULT_FLAGS.put("settings_contextual_home2", "false"); } diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java index f406be9ebac7..e05c3743565c 100644 --- a/core/java/android/view/WindowContainerTransaction.java +++ b/core/java/android/view/WindowContainerTransaction.java @@ -72,6 +72,33 @@ public class WindowContainerTransaction implements Parcelable { } /** + * Resize a container's app bounds. This is the bounds used to report appWidth/Height to an + * app's DisplayInfo. It is derived by subtracting the overlapping portion of the navbar from + * the full bounds. + */ + public WindowContainerTransaction setAppBounds(IWindowContainer container, Rect appBounds) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mConfiguration.windowConfiguration.setAppBounds(appBounds); + chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION; + chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; + return this; + } + + /** + * Resize a container's configuration size. The configuration size is what gets reported to the + * app via screenWidth/HeightDp and influences which resources get loaded. This size is + * derived by subtracting the overlapping portions of both the statusbar and the navbar from + * the full bounds. + */ + public WindowContainerTransaction setScreenSizeDp(IWindowContainer container, int w, int h) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mConfiguration.screenWidthDp = w; + chg.mConfiguration.screenHeightDp = h; + chg.mConfigSetMask |= ActivityInfo.CONFIG_SCREEN_SIZE; + return this; + } + + /** * Notify activies within the hiearchy of a container that they have entered picture-in-picture * mode with the given bounds. */ @@ -161,7 +188,8 @@ public class WindowContainerTransaction implements Parcelable { @Override public String toString() { - return "WindowContainerTransaction { changes = " + mChanges + " }"; + return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps + + " }"; } @Override @@ -269,6 +297,11 @@ public class WindowContainerTransaction implements Parcelable { (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0); + final boolean changesAppBounds = + (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 + && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS) + != 0); + final boolean changesSs = (mConfigSetMask & ActivityInfo.CONFIG_SCREEN_SIZE) != 0; final boolean changesSss = (mConfigSetMask & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0; StringBuilder sb = new StringBuilder(); @@ -276,9 +309,16 @@ public class WindowContainerTransaction implements Parcelable { if (changesBounds) { sb.append("bounds:" + mConfiguration.windowConfiguration.getBounds() + ","); } + if (changesAppBounds) { + sb.append("appbounds:" + mConfiguration.windowConfiguration.getAppBounds() + ","); + } if (changesSss) { sb.append("ssw:" + mConfiguration.smallestScreenWidthDp + ","); } + if (changesSs) { + sb.append("sw/h:" + mConfiguration.screenWidthDp + "x" + + mConfiguration.screenHeightDp + ","); + } if ((mChangeMask & CHANGE_FOCUSABLE) != 0) { sb.append("focusable:" + mFocusable + ","); } diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index bcf731d993df..d50826f815a0 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -299,11 +299,8 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { .createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED) .setStrings(getMetricsCategory()) .write(); - showEmptyState(activeListAdapter, - R.drawable.ic_work_apps_off, - R.string.resolver_turn_on_work_apps, - R.string.resolver_turn_on_work_apps_explanation, - (View.OnClickListener) v -> { + showWorkProfileOffEmptyState(activeListAdapter, + v -> { ProfileDescriptor descriptor = getItem( userHandleToPageIndex(activeListAdapter.getUserHandle())); showSpinner(descriptor.getEmptyStateView()); @@ -319,27 +316,29 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL) .setStrings(getMetricsCategory()) .write(); - showEmptyState(activeListAdapter, - R.drawable.ic_sharing_disabled, - R.string.resolver_cant_share_with_personal_apps, - R.string.resolver_cant_share_cross_profile_explanation); + showNoWorkToPersonalIntentsEmptyState(activeListAdapter); } else { DevicePolicyEventLogger.createEvent( DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK) .setStrings(getMetricsCategory()) .write(); - showEmptyState(activeListAdapter, - R.drawable.ic_sharing_disabled, - R.string.resolver_cant_share_with_work_apps, - R.string.resolver_cant_share_cross_profile_explanation); + showNoPersonalToWorkIntentsEmptyState(activeListAdapter); } return false; } } - showListView(activeListAdapter); return activeListAdapter.rebuildList(doPostProcessing); } + protected abstract void showWorkProfileOffEmptyState( + ResolverListAdapter activeListAdapter, View.OnClickListener listener); + + protected abstract void showNoPersonalToWorkIntentsEmptyState( + ResolverListAdapter activeListAdapter); + + protected abstract void showNoWorkToPersonalIntentsEmptyState( + ResolverListAdapter activeListAdapter); + void showEmptyState(ResolverListAdapter listAdapter) { UserHandle listUserHandle = listAdapter.getUserHandle(); if (UserHandle.myUserId() == listUserHandle.getIdentifier() @@ -353,16 +352,16 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { showEmptyState(listAdapter, R.drawable.ic_no_apps, R.string.resolver_no_apps_available, - R.string.resolver_no_apps_available_explanation); + /* subtitleRes */ 0); } } - private void showEmptyState(ResolverListAdapter activeListAdapter, + protected void showEmptyState(ResolverListAdapter activeListAdapter, @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes) { showEmptyState(activeListAdapter, iconRes, titleRes, subtitleRes, /* buttonOnClick */ null); } - private void showEmptyState(ResolverListAdapter activeListAdapter, + protected void showEmptyState(ResolverListAdapter activeListAdapter, @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes, View.OnClickListener buttonOnClick) { ProfileDescriptor descriptor = getItem( @@ -379,11 +378,18 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { title.setText(titleRes); TextView subtitle = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle); - subtitle.setText(subtitleRes); + if (subtitleRes != 0) { + subtitle.setVisibility(View.VISIBLE); + subtitle.setText(subtitleRes); + } else { + subtitle.setVisibility(View.GONE); + } Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button); button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE); button.setOnClickListener(buttonOnClick); + + activeListAdapter.markTabLoaded(); } private void showSpinner(View emptyStateView) { @@ -403,7 +409,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { emptyStateView.findViewById(R.id.resolver_empty_state_progress).setVisibility(View.GONE); } - private void showListView(ResolverListAdapter activeListAdapter) { + protected void showListView(ResolverListAdapter activeListAdapter) { ProfileDescriptor descriptor = getItem( userHandleToPageIndex(activeListAdapter.getUserHandle())); descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.VISIBLE); diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java index b39d42ec1e96..2167b1ebd473 100644 --- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.content.Context; import android.os.UserHandle; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; import com.android.internal.R; @@ -169,6 +170,32 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd return ResolverActivity.METRICS_CATEGORY_CHOOSER; } + @Override + protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter, + View.OnClickListener listener) { + showEmptyState(activeListAdapter, + R.drawable.ic_work_apps_off, + R.string.resolver_turn_on_work_apps_share, + /* subtitleRes */ 0, + listener); + } + + @Override + protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) { + showEmptyState(activeListAdapter, + R.drawable.ic_sharing_disabled, + R.string.resolver_cant_share_with_work_apps, + R.string.resolver_cant_share_cross_profile_explanation); + } + + @Override + protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) { + showEmptyState(activeListAdapter, + R.drawable.ic_sharing_disabled, + R.string.resolver_cant_share_with_personal_apps, + R.string.resolver_cant_share_cross_profile_explanation); + } + class ChooserProfileDescriptor extends ProfileDescriptor { private ChooserActivity.ChooserGridAdapter chooserGridAdapter; private RecyclerView recyclerView; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 6ab61850480d..ec371d9a7311 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -163,7 +163,7 @@ public class ResolverActivity extends Activity implements * TODO(arangelov): Remove a couple of weeks after work/personal tabs are finalized. */ @VisibleForTesting - public static boolean ENABLE_TABBED_VIEW = false; + public static boolean ENABLE_TABBED_VIEW = true; private static final String TAB_TAG_PERSONAL = "personal"; private static final String TAB_TAG_WORK = "work"; @@ -989,6 +989,8 @@ public class ResolverActivity extends Activity implements } if (shouldShowEmptyState(listAdapter)) { mMultiProfilePagerAdapter.showEmptyState(listAdapter); + } else { + mMultiProfilePagerAdapter.showListView(listAdapter); } if (doPostProcessing) { if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier() @@ -1337,9 +1339,12 @@ public class ResolverActivity extends Activity implements + "cannot be null."); } // We partially rebuild the inactive adapter to determine if we should auto launch - boolean rebuildCompleted = mMultiProfilePagerAdapter.rebuildActiveTab(true); + // isTabLoaded will be true here if the empty state screen is shown instead of the list. + boolean rebuildCompleted = mMultiProfilePagerAdapter.rebuildActiveTab(true) + || mMultiProfilePagerAdapter.getActiveListAdapter().isTabLoaded(); if (shouldShowTabs()) { - boolean rebuildInactiveCompleted = mMultiProfilePagerAdapter.rebuildInactiveTab(false); + boolean rebuildInactiveCompleted = mMultiProfilePagerAdapter.rebuildInactiveTab(false) + || mMultiProfilePagerAdapter.getInactiveListAdapter().isTabLoaded(); rebuildCompleted = rebuildCompleted && rebuildInactiveCompleted; } @@ -1399,8 +1404,8 @@ public class ResolverActivity extends Activity implements if (numberOfProfiles == 1 && maybeAutolaunchIfSingleTarget()) { return true; } else if (numberOfProfiles == 2 - && mMultiProfilePagerAdapter.getActiveListAdapter().isListLoaded() - && mMultiProfilePagerAdapter.getInactiveListAdapter().isListLoaded() + && mMultiProfilePagerAdapter.getActiveListAdapter().isTabLoaded() + && mMultiProfilePagerAdapter.getInactiveListAdapter().isTabLoaded() && (maybeAutolaunchIfNoAppsOnInactiveTab() || maybeAutolaunchIfCrossProfileSupported())) { return true; @@ -1595,9 +1600,17 @@ public class ResolverActivity extends Activity implements } private void resetTabsHeaderStyle(TabWidget tabWidget) { + String workContentDescription = getString(R.string.resolver_work_tab_accessibility); + String personalContentDescription = getString(R.string.resolver_personal_tab_accessibility); for (int i = 0; i < tabWidget.getChildCount(); i++) { - TextView title = tabWidget.getChildAt(i).findViewById(android.R.id.title); + View tabView = tabWidget.getChildAt(i); + TextView title = tabView.findViewById(android.R.id.title); title.setTextColor(getColor(R.color.resolver_tabs_inactive_color)); + if (title.getText().equals(getString(R.string.resolver_personal_tab))) { + tabView.setContentDescription(personalContentDescription); + } else if (title.getText().equals(getString(R.string.resolver_work_tab))) { + tabView.setContentDescription(workContentDescription); + } } } diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index 54453d0d0f46..61a404e3bc5f 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -87,7 +87,7 @@ public class ResolverListAdapter extends BaseAdapter { private final ResolverListCommunicator mResolverListCommunicator; private Runnable mPostListReadyRunnable; private final boolean mIsAudioCaptureDevice; - private boolean mIsListLoaded; + private boolean mIsTabLoaded; public ResolverListAdapter(Context context, List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, @@ -192,7 +192,7 @@ public class ResolverListAdapter extends BaseAdapter { mLastChosenPosition = -1; mAllTargetsAreBrowsers = false; mDisplayList.clear(); - mIsListLoaded = false; + mIsTabLoaded = false; if (mBaseResolveList != null) { currentResolveList = mUnfilteredResolveList = new ArrayList<>(); @@ -354,7 +354,7 @@ public class ResolverListAdapter extends BaseAdapter { mResolverListCommunicator.sendVoiceChoicesIfNeeded(); postListReadyRunnable(doPostProcessing); - mIsListLoaded = true; + mIsTabLoaded = true; } /** @@ -614,8 +614,12 @@ public class ResolverListAdapter extends BaseAdapter { return mIntents; } - protected boolean isListLoaded() { - return mIsListLoaded; + protected boolean isTabLoaded() { + return mIsTabLoaded; + } + + protected void markTabLoaded() { + mIsTabLoaded = true; } /** diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java index f6382d397d6f..0440f5e92ce4 100644 --- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.content.Context; import android.os.UserHandle; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; import android.widget.ListView; @@ -161,6 +162,32 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA return ResolverActivity.METRICS_CATEGORY_RESOLVER; } + @Override + protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter, + View.OnClickListener listener) { + showEmptyState(activeListAdapter, + R.drawable.ic_work_apps_off, + R.string.resolver_turn_on_work_apps_view, + /* subtitleRes */ 0, + listener); + } + + @Override + protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) { + showEmptyState(activeListAdapter, + R.drawable.ic_sharing_disabled, + R.string.resolver_cant_access_work_apps, + R.string.resolver_cant_access_work_apps_explanation); + } + + @Override + protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) { + showEmptyState(activeListAdapter, + R.drawable.ic_sharing_disabled, + R.string.resolver_cant_access_personal_apps, + R.string.resolver_cant_access_personal_apps_explanation); + } + class ResolverProfileDescriptor extends ProfileDescriptor { private ResolverListAdapter resolverListAdapter; final ListView listView; diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java index 16628d71712c..ab890d277f43 100644 --- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java +++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java @@ -30,6 +30,7 @@ public class CompatibilityChangeInfo implements Parcelable { private final @Nullable String mName; private final int mEnableAfterTargetSdk; private final boolean mDisabled; + private final boolean mLoggingOnly; private final @Nullable String mDescription; public long getId() { @@ -49,17 +50,22 @@ public class CompatibilityChangeInfo implements Parcelable { return mDisabled; } + public boolean getLoggingOnly() { + return mLoggingOnly; + } + public String getDescription() { return mDescription; } public CompatibilityChangeInfo( Long changeId, String name, int enableAfterTargetSdk, boolean disabled, - String description) { + boolean loggingOnly, String description) { this.mChangeId = changeId; this.mName = name; this.mEnableAfterTargetSdk = enableAfterTargetSdk; this.mDisabled = disabled; + this.mLoggingOnly = loggingOnly; this.mDescription = description; } @@ -68,6 +74,7 @@ public class CompatibilityChangeInfo implements Parcelable { mName = in.readString(); mEnableAfterTargetSdk = in.readInt(); mDisabled = in.readBoolean(); + mLoggingOnly = in.readBoolean(); mDescription = in.readString(); } @@ -82,6 +89,7 @@ public class CompatibilityChangeInfo implements Parcelable { dest.writeString(mName); dest.writeInt(mEnableAfterTargetSdk); dest.writeBoolean(mDisabled); + dest.writeBoolean(mLoggingOnly); dest.writeString(mDescription); } diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java index 56216c251070..bed41b37ac81 100644 --- a/core/java/com/android/internal/compat/OverrideAllowedState.java +++ b/core/java/com/android/internal/compat/OverrideAllowedState.java @@ -33,7 +33,8 @@ public final class OverrideAllowedState implements Parcelable { DISABLED_NOT_DEBUGGABLE, DISABLED_NON_TARGET_SDK, DISABLED_TARGET_SDK_TOO_HIGH, - PACKAGE_DOES_NOT_EXIST + PACKAGE_DOES_NOT_EXIST, + LOGGING_ONLY_CHANGE }) @Retention(RetentionPolicy.SOURCE) public @interface State { @@ -60,6 +61,10 @@ public final class OverrideAllowedState implements Parcelable { * Package does not exist. */ public static final int PACKAGE_DOES_NOT_EXIST = 4; + /** + * Change is marked as logging only, and cannot be toggled. + */ + public static final int LOGGING_ONLY_CHANGE = 5; @State public final int state; @@ -118,6 +123,10 @@ public final class OverrideAllowedState implements Parcelable { "Cannot override %1$d for %2$s because the package does not exist, and " + "the change is targetSdk gated.", changeId, packageName)); + case LOGGING_ONLY_CHANGE: + throw new SecurityException(String.format( + "Cannot override %1$d because it is marked as a logging-only change.", + changeId)); } } diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index 6d2d7356a2e8..7dc38711a6ef 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -232,7 +232,7 @@ public class VpnConfig implements Parcelable { .append(", allowIPv4=").append(allowIPv4) .append(", allowIPv6=").append(allowIPv6) .append(", underlyingNetworks=").append(Arrays.toString(underlyingNetworks)) - .append(", proxyInfo=").append(proxyInfo.toString()) + .append(", proxyInfo=").append(proxyInfo) .append("}") .toString(); } diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index b117e40cdbb6..c83cab77dd13 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -121,7 +121,8 @@ public class ResolverDrawerLayout extends ViewGroup { private final Rect mTempRect = new Rect(); - private AbsListView mNestedScrollingChild; + private AbsListView mNestedListChild; + private RecyclerView mNestedRecyclerChild; private final ViewTreeObserver.OnTouchModeChangeListener mTouchModeChangeListener = new ViewTreeObserver.OnTouchModeChangeListener() { @@ -347,11 +348,20 @@ public class ResolverDrawerLayout extends ViewGroup { return mIsDragging || mOpenOnClick; } - private boolean isNestedChildScrolled() { - return mNestedScrollingChild != null - && mNestedScrollingChild.getChildCount() > 0 - && (mNestedScrollingChild.getFirstVisiblePosition() > 0 - || mNestedScrollingChild.getChildAt(0).getTop() < 0); + private boolean isNestedListChildScrolled() { + return mNestedListChild != null + && mNestedListChild.getChildCount() > 0 + && (mNestedListChild.getFirstVisiblePosition() > 0 + || mNestedListChild.getChildAt(0).getTop() < 0); + } + + private boolean isNestedRecyclerChildScrolled() { + if (mNestedRecyclerChild != null && mNestedRecyclerChild.getChildCount() > 0) { + final RecyclerView.ViewHolder vh = + mNestedRecyclerChild.findViewHolderForAdapterPosition(0); + return vh == null || vh.itemView.getTop() < 0; + } + return false; } @Override @@ -396,8 +406,10 @@ public class ResolverDrawerLayout extends ViewGroup { } if (mIsDragging) { final float dy = y - mLastTouchY; - if (dy > 0 && isNestedChildScrolled()) { - mNestedScrollingChild.smoothScrollBy((int) -dy, 0); + if (dy > 0 && isNestedListChildScrolled()) { + mNestedListChild.smoothScrollBy((int) -dy, 0); + } else if (dy > 0 && isNestedRecyclerChildScrolled()) { + mNestedRecyclerChild.scrollBy(0, (int) -dy); } else { performDrag(dy); } @@ -452,8 +464,10 @@ public class ResolverDrawerLayout extends ViewGroup { smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel); mDismissOnScrollerFinished = true; } else { - if (isNestedChildScrolled()) { - mNestedScrollingChild.smoothScrollToPosition(0); + if (isNestedListChildScrolled()) { + mNestedListChild.smoothScrollToPosition(0); + } else if (isNestedRecyclerChildScrolled()) { + mNestedRecyclerChild.smoothScrollToPosition(0); } smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel); } @@ -729,8 +743,11 @@ public class ResolverDrawerLayout extends ViewGroup { @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { if ((nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0) { - if (child instanceof AbsListView) { - mNestedScrollingChild = (AbsListView) child; + if (target instanceof AbsListView) { + mNestedListChild = (AbsListView) target; + } + if (target instanceof RecyclerView) { + mNestedRecyclerChild = (RecyclerView) target; } return true; } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 9ddaa981c382..189a0a72e42c 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -140,6 +140,7 @@ cc_library_shared { "android_media_AudioEffectDescriptor.cpp", "android_media_AudioRecord.cpp", "android_media_AudioSystem.cpp", + "android_media_AudioTrackCallback.cpp", "android_media_AudioTrack.cpp", "android_media_AudioAttributes.cpp", "android_media_AudioProductStrategies.cpp", diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index a888b43a4854..13518186376d 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -31,13 +31,14 @@ #include <binder/MemoryHeapBase.h> #include <binder/MemoryBase.h> -#include "android_media_AudioFormat.h" +#include "android_media_AudioAttributes.h" #include "android_media_AudioErrors.h" +#include "android_media_AudioFormat.h" +#include "android_media_AudioTrackCallback.h" +#include "android_media_DeviceCallback.h" #include "android_media_MediaMetricsJNI.h" #include "android_media_PlaybackParams.h" -#include "android_media_DeviceCallback.h" #include "android_media_VolumeShaper.h" -#include "android_media_AudioAttributes.h" #include <cinttypes> @@ -75,22 +76,12 @@ struct audiotrack_callback_cookie { // ---------------------------------------------------------------------------- class AudioTrackJniStorage { - public: - sp<MemoryHeapBase> mMemHeap; - sp<MemoryBase> mMemBase; - audiotrack_callback_cookie mCallbackData; - sp<JNIDeviceCallback> mDeviceCallback; - - AudioTrackJniStorage() { - mCallbackData.audioTrack_class = 0; - mCallbackData.audioTrack_ref = 0; - mCallbackData.isOffload = false; - } - - ~AudioTrackJniStorage() { - mMemBase.clear(); - mMemHeap.clear(); - } +public: + sp<MemoryHeapBase> mMemHeap; + sp<MemoryBase> mMemBase; + audiotrack_callback_cookie mCallbackData{}; + sp<JNIDeviceCallback> mDeviceCallback; + sp<JNIAudioTrackCallback> mAudioTrackCallback; bool allocSharedMem(int sizeInBytes) { mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base"); @@ -459,6 +450,10 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); lpJniStorage->mCallbackData.busy = false; } + lpJniStorage->mAudioTrackCallback = + new JNIAudioTrackCallback(env, thiz, lpJniStorage->mCallbackData.audioTrack_ref, + javaAudioTrackFields.postNativeEventInJava); + lpTrack->setAudioTrackCallback(lpJniStorage->mAudioTrackCallback); nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); if (nSession == NULL) { diff --git a/core/jni/android_media_AudioTrackCallback.cpp b/core/jni/android_media_AudioTrackCallback.cpp new file mode 100644 index 000000000000..d97566b87adc --- /dev/null +++ b/core/jni/android_media_AudioTrackCallback.cpp @@ -0,0 +1,99 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 + +#define LOG_TAG "AudioTrackCallback-JNI" + +#include <algorithm> + +#include <nativehelper/JNIHelp.h> +#include <utils/Errors.h> +#include <utils/Log.h> + +#include "android_media_AudioTrackCallback.h" +#include "core_jni_helpers.h" + +using namespace android; + +#define BYTE_BUFFER_NAME "java/nio/ByteBuffer" +#define BYTE_BUFFER_ALLOCATE_DIRECT_NAME "allocateDirect" + +JNIAudioTrackCallback::JNIAudioTrackCallback(JNIEnv* env, jobject thiz, jobject weak_thiz, + jmethodID postEventFromNative) { + // Hold onto the AudioTrack class for use in calling the static method + // that posts events to the application thread. + jclass clazz = env->GetObjectClass(thiz); + if (clazz == nullptr) { + return; + } + mClass = (jclass)env->NewGlobalRef(clazz); + + // We use a weak reference so the AudioTrack object can be garbage collected. + // The reference is only used as a proxy for callbacks. + mObject = env->NewGlobalRef(weak_thiz); + + mPostEventFromNative = postEventFromNative; + + jclass byteBufferClass = FindClassOrDie(env, BYTE_BUFFER_NAME); + mByteBufferClass = (jclass)env->NewGlobalRef(byteBufferClass); + mAllocateDirectMethod = + GetStaticMethodIDOrDie(env, mByteBufferClass, BYTE_BUFFER_ALLOCATE_DIRECT_NAME, + "(I)Ljava/nio/ByteBuffer;"); +} + +JNIAudioTrackCallback::~JNIAudioTrackCallback() { + // remove global references + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (env == nullptr) { + return; + } + env->DeleteGlobalRef(mObject); + env->DeleteGlobalRef(mClass); + env->DeleteGlobalRef(mByteBufferClass); +} + +binder::Status JNIAudioTrackCallback::onCodecFormatChanged( + const std::vector<uint8_t>& audioMetadata) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (env == nullptr) { + return binder::Status::ok(); + } + + jobject byteBuffer = env->CallStaticObjectMethod(mByteBufferClass, mAllocateDirectMethod, + (jint)audioMetadata.size()); + if (env->ExceptionCheck()) { + ALOGW("An exception occurred while allocating direct buffer"); + env->ExceptionDescribe(); + env->ExceptionClear(); + } + if (byteBuffer == nullptr) { + ALOGE("Failed allocating a direct ByteBuffer"); + return binder::Status::fromStatusT(NO_MEMORY); + } + + uint8_t* byteBufferAddr = (uint8_t*)env->GetDirectBufferAddress(byteBuffer); + std::copy(audioMetadata.begin(), audioMetadata.end(), byteBufferAddr); + env->CallStaticVoidMethod(mClass, mPostEventFromNative, mObject, + AUDIO_NATIVE_EVENT_CODEC_FORMAT_CHANGE, 0, 0, byteBuffer); + if (env->ExceptionCheck()) { + ALOGW("An exception occurred while notifying codec format changed."); + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + return binder::Status::ok(); +} diff --git a/core/jni/android_media_AudioTrackCallback.h b/core/jni/android_media_AudioTrackCallback.h new file mode 100644 index 000000000000..3b8a8bb1bba8 --- /dev/null +++ b/core/jni/android_media_AudioTrackCallback.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef ANDROID_MEDIA_AUDIO_TRACK_CALLBACK_H +#define ANDROID_MEDIA_AUDIO_TRACK_CALLBACK_H + +#include <android/media/BnAudioTrackCallback.h> + +namespace android { + +#define AUDIO_NATIVE_EVENT_CODEC_FORMAT_CHANGE 100 + +// TODO(b/149870866) : Extract common part for JNIAudioTrackCallback and JNIDeviceCallback +class JNIAudioTrackCallback : public media::BnAudioTrackCallback { +public: + JNIAudioTrackCallback(JNIEnv* env, jobject thiz, jobject weak_thiz, + jmethodID postEventFromNative); + ~JNIAudioTrackCallback() override; + + binder::Status onCodecFormatChanged(const std::vector<uint8_t>& audioMetadata) override; + +private: + jclass mClass; // Reference to AudioTrack class + jobject mObject; // Weak ref to AudioTrack Java object to call on + jmethodID mPostEventFromNative; // postEventFromNative method ID + jclass mByteBufferClass; // Reference to ByteBuffer class + jmethodID mAllocateDirectMethod; // ByteBuffer.allocateDirect method ID +}; + +}; // namespace android + +#endif // ANDROID_MEDIA_AUDIO_TRACK_CALLBACK_H diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 6d9e8ab776b7..a3313b21131d 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -77,6 +77,7 @@ message SecureSettingsProto { optional SettingProto interactive_ui_timeout_ms = 33 [ (android.privacy).dest = DEST_AUTOMATIC ]; // Settings for magnification mode optional SettingProto accessibility_magnification_mode = 34 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto button_long_press_targets = 35 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Accessibility accessibility = 2; diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2356b1737acd..addcd81ae5c8 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5385,26 +5385,36 @@ <xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string> <!-- ResolverActivity - profile tabs --> - <!-- Label for the perosnal tab of the share sheet and intent resolver [CHAR LIMIT=NONE] --> + <!-- 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> - <!-- Label for the work tab of the share sheet and intent resolver [CHAR LIMIT=NONE] --> + <!-- Label of a tab on a screen. A user can tap this tab to switch to the 'Work' view (that shows their work content) if they have a work profile on their device. [CHAR LIMIT=NONE] --> <string name="resolver_work_tab">Work</string> - <!-- Label to indicate that the user cannot share with work apps [CHAR LIMIT=NONE] --> + <!-- Accessibility label for the personal tab button. [CHAR LIMIT=NONE] --> + <string name="resolver_personal_tab_accessibility">Personal view</string> + <!-- Accessibility label for the work tab button. [CHAR LIMIT=NONE] --> + <string name="resolver_work_tab_accessibility">Work view</string> + <!-- Title of a screen. This text lets the user know that their IT admin doesn't allow them to share personal content with work apps. [CHAR LIMIT=NONE] --> <string name="resolver_cant_share_with_work_apps">Can\u2019t share with work apps</string> - <!-- Label to indicate that the user cannot share with personal apps [CHAR LIMIT=NONE] --> + <!-- Title of a screen. This text lets the user know that their IT admin doesn't allow them to share work content with personal apps. [CHAR LIMIT=NONE] --> <string name="resolver_cant_share_with_personal_apps">Can\u2019t share with personal apps</string> - <!-- Label to explain to the user that sharing between personal and work apps is not enabled by the IT admin [CHAR LIMIT=NONE] --> - <string name="resolver_cant_share_cross_profile_explanation">Your IT admin blocked sharing between personal and work apps</string> - <!-- Label to indicate that the user has to turn on work apps in order to access work data [CHAR LIMIT=NONE] --> - <string name="resolver_turn_on_work_apps">Turn on work apps</string> - <!-- Label to explain that the user has to turn on work apps in order to access work data [CHAR LIMIT=NONE] --> - <string name="resolver_turn_on_work_apps_explanation">Turn on work apps to access work apps & contacts</string> - <!-- Label to indicate that no apps are resolved [CHAR LIMIT=NONE] --> + <!-- Error message. This text is explaining that the user's IT admin doesn't allow sharing between personal and work apps. [CHAR LIMIT=NONE] --> + <string name="resolver_cant_share_cross_profile_explanation">Your IT admin blocked sharing between personal and work profiles</string> + <!-- Title of an error screen. This error message lets the user know that their IT admin blocked access to work apps, and the personal content that they're trying to view in a work app, such as Chrome, isn't allowed. [CHAR LIMIT=NONE] --> + <string name="resolver_cant_access_work_apps">Can\u2019t access work apps</string> + <!-- Error message. This message lets the user know that their IT admin blocked access to work apps, and the personal content that they're trying to view in a work app, such as Chrome, isn't allowed. [CHAR LIMIT=NONE] --> + <string name="resolver_cant_access_work_apps_explanation">Your IT admin doesn\u2019t let you view personal content in work apps</string> + <!-- Title of an error screen. This error message lets the user know that their IT admin blocked access to personal apps, and the work content that they're trying to view in a personal app, such as Chrome, isn't allowed. [CHAR LIMIT=NONE] --> + <string name="resolver_cant_access_personal_apps">Can\u2019t access personal apps</string> + <!-- Error message. This message lets the user know that their IT admin blocked access to personal apps, and the work content that they're trying to view in a personal app, such as Chrome, isn't allowed. [CHAR LIMIT=NONE] --> + <string name="resolver_cant_access_personal_apps_explanation">Your IT admin doesn\u2019t let you view work content in personal apps</string> + <!-- Error message. This text lets the user know that they need to turn on work apps in order to share content. There's also a button a user can tap to turn on the apps. [CHAR LIMIT=NONE] --> + <string name="resolver_turn_on_work_apps_share">Turn on work profile to share content</string> + <!-- Error message. This text lets the user know that they need to turn on work apps in order to view content. There's also a button a user can tap to turn on the apps. [CHAR LIMIT=NONE] --> + <string name="resolver_turn_on_work_apps_view">Turn on work profile to view content</string> + <!-- Error message. This text lets the user know that work apps aren't available on the device. [CHAR LIMIT=NONE] --> <string name="resolver_no_apps_available">No apps available</string> - <!-- Label to explain that no apps are resolved [CHAR LIMIT=NONE] --> - <string name="resolver_no_apps_available_explanation">We couldn\u2019t find any apps</string> - <!-- Button which switches on the disabled work profile [CHAR LIMIT=NONE] --> - <string name="resolver_switch_on_work">Switch on work</string> + <!-- Button text. This button turns on a user's work profile so they can access their work apps and data. [CHAR LIMIT=NONE] --> + <string name="resolver_switch_on_work">Turn on</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> <string name="permlab_accessCallAudio">Record or play audio in telephony calls</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c1d8168e6287..0971aadfe4ab 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3874,7 +3874,9 @@ <java-symbol type="color" name="resolver_tabs_active_color" /> <java-symbol type="color" name="resolver_tabs_inactive_color" /> <java-symbol type="string" name="resolver_personal_tab" /> + <java-symbol type="string" name="resolver_personal_tab_accessibility" /> <java-symbol type="string" name="resolver_work_tab" /> + <java-symbol type="string" name="resolver_work_tab_accessibility" /> <java-symbol type="id" name="stub" /> <java-symbol type="id" name="resolver_empty_state" /> <java-symbol type="id" name="resolver_empty_state_icon" /> @@ -3886,11 +3888,13 @@ <java-symbol type="string" name="resolver_cant_share_with_work_apps" /> <java-symbol type="string" name="resolver_cant_share_with_personal_apps" /> <java-symbol type="string" name="resolver_cant_share_cross_profile_explanation" /> - <java-symbol type="string" name="resolver_turn_on_work_apps" /> - <java-symbol type="string" name="resolver_turn_on_work_apps_explanation" /> + <java-symbol type="string" name="resolver_cant_access_work_apps" /> + <java-symbol type="string" name="resolver_cant_access_work_apps_explanation" /> + <java-symbol type="string" name="resolver_cant_access_personal_apps" /> + <java-symbol type="string" name="resolver_cant_access_personal_apps_explanation" /> + <java-symbol type="string" name="resolver_turn_on_work_apps_view" /> + <java-symbol type="string" name="resolver_turn_on_work_apps_share" /> <java-symbol type="string" name="resolver_no_apps_available" /> - <java-symbol type="string" name="resolver_no_apps_available_explanation" /> - <java-symbol type="string" name="resolver_no_apps_available_explanation" /> <java-symbol type="string" name="resolver_switch_on_work" /> <java-symbol type="drawable" name="ic_work_apps_off" /> <java-symbol type="drawable" name="ic_sharing_disabled" /> diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index 6d0e58bc89be..24e96d4b8463 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -1340,7 +1340,7 @@ public class ChooserActivityTest { onView(withText(R.string.resolver_work_tab)).perform(click()); waitForIdle(); - onView(withText(R.string.resolver_turn_on_work_apps)) + onView(withText(R.string.resolver_turn_on_work_apps_share)) .check(matches(isDisplayed())); } diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index a7bf48858e42..9d1ca615e54b 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -624,7 +624,7 @@ public class ResolverActivityTest { onView(withText(R.string.resolver_work_tab)).perform(click()); waitForIdle(); - onView(withText(R.string.resolver_turn_on_work_apps)) + onView(withText(R.string.resolver_turn_on_work_apps_view)) .check(matches(isDisplayed())); } diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp new file mode 100644 index 000000000000..308c1a59a7aa --- /dev/null +++ b/libs/WindowManager/Jetpack/Android.bp @@ -0,0 +1,38 @@ +// 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_library_import { + name: "window-extensions", + aars: ["window-extensions-release.aar"], + sdk_version: "current", +} + +java_library { + name: "androidx.window.extensions", + srcs: ["src/**/*.java"], + static_libs: ["window-extensions"], + installable: true, + sdk_version: "core_platform", + vendor: true, + libs: ["framework", "androidx.annotation_annotation",], + required: ["androidx.window.extensions.xml",], +} + +prebuilt_etc { + name: "androidx.window.extensions.xml", + vendor: true, + sub_dir: "permissions", + src: "androidx.window.extensions.xml", + filename_from_src: true, +} diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml new file mode 100644 index 000000000000..1f0ff6656de0 --- /dev/null +++ b/libs/WindowManager/Jetpack/androidx.window.extensions.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> +<permissions> + <library + name="androidx.window.extensions" + file="/vendor/framework/androidx.window.extensions.jar"/> +</permissions> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java new file mode 100644 index 000000000000..c4f11a0a370c --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java @@ -0,0 +1,130 @@ +/* + * 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 androidx.window.extensions; + +import static android.view.Display.INVALID_DISPLAY; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import android.app.Activity; +import android.app.ActivityThread; +import android.graphics.Rect; +import android.hardware.display.DisplayManagerGlobal; +import android.os.IBinder; +import android.view.DisplayInfo; +import android.view.Surface; + +/** + * Toolkit class for calculation of the display feature bounds within the window. + * NOTE: This sample implementation only works for Activity windows, because there is no public APIs + * to obtain layout params or bounds for arbitrary windows. + */ +class ExtensionHelper { + /** + * Rotate the input rectangle specified in default display orientation to the current display + * rotation. + */ + static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) { + DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance(); + DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId); + int rotation = displayInfo.rotation; + + boolean isSideRotation = rotation == ROTATION_90 || rotation == ROTATION_270; + int displayWidth = isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth; + int displayHeight = isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight; + + inOutRect.intersect(0, 0, displayWidth, displayHeight); + + rotateBounds(inOutRect, displayWidth, displayHeight, rotation); + } + + /** + * Rotate the input rectangle within parent bounds for a given delta. + */ + private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight, + @Surface.Rotation int delta) { + int origLeft = inOutRect.left; + switch (delta) { + case ROTATION_0: + return; + case ROTATION_90: + inOutRect.left = inOutRect.top; + inOutRect.top = parentWidth - inOutRect.right; + inOutRect.right = inOutRect.bottom; + inOutRect.bottom = parentWidth - origLeft; + return; + case ROTATION_180: + inOutRect.left = parentWidth - inOutRect.right; + inOutRect.right = parentWidth - origLeft; + return; + case ROTATION_270: + inOutRect.left = parentHeight - inOutRect.bottom; + inOutRect.bottom = inOutRect.right; + inOutRect.right = parentHeight - inOutRect.top; + inOutRect.top = origLeft; + return; + } + } + + /** Transform rectangle from absolute coordinate space to the window coordinate space. */ + static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) { + Rect windowRect = getWindowRect(windowToken); + if (windowRect == null) { + inOutRect.setEmpty(); + return; + } + if (!Rect.intersects(inOutRect, windowRect)) { + inOutRect.setEmpty(); + return; + } + inOutRect.intersect(windowRect); + inOutRect.offset(-windowRect.left, -windowRect.top); + } + + /** + * Get the current window bounds in absolute coordinates. + * NOTE: Only works with Activity windows. + */ + private static Rect getWindowRect(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + final Rect windowRect = new Rect(); + if (activity != null) { + activity.getWindow().getDecorView().getWindowDisplayFrame(windowRect); + } + return windowRect; + } + + /** + * Check if this window is an Activity window that is in multi-window mode. + */ + static boolean isInMultiWindow(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + return activity != null && activity.isInMultiWindowMode(); + } + + /** + * Get the id of the parent display for the window. + * NOTE: Only works with Activity windows. + */ + static int getWindowDisplay(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + return activity != null + ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java new file mode 100644 index 000000000000..47349f11fb93 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java @@ -0,0 +1,42 @@ +/* + * 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 androidx.window.extensions; + +import android.content.Context; + +/** + * Provider class that will instantiate the library implementation. It must be included in the + * vendor library, and the vendor implementation must match the signature of this class. + */ +public class ExtensionProvider { + + /** + * The support library will instantiate the vendor implementation using this interface. + * @return An implementation of {@link ExtensionInterface}. + */ + public static ExtensionInterface getExtensionImpl(Context context) { + return new SettingsExtensionImpl(context); + } + + /** + * The support library will use this method to check API version compatibility. + * @return API version string in MAJOR.MINOR.PATCH-description format. + */ + public static String getApiVersion() { + return "1.0.0-settings_sample"; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java new file mode 100644 index 000000000000..7a3fbf3ad9b8 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java @@ -0,0 +1,217 @@ +/* + * 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 androidx.window.extensions; + +import static android.view.Display.DEFAULT_DISPLAY; + +import static androidx.window.extensions.ExtensionHelper.getWindowDisplay; +import static androidx.window.extensions.ExtensionHelper.isInMultiWindow; +import static androidx.window.extensions.ExtensionHelper.rotateRectToDisplayRotation; +import static androidx.window.extensions.ExtensionHelper.transformToWindowSpaceRect; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.graphics.Rect; +import android.net.Uri; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class SettingsExtensionImpl extends StubExtension { + private static final String TAG = "SettingsExtension"; + + private static final String DEVICE_POSTURE = "device_posture"; + private static final String DISPLAY_FEATURES = "display_features"; + + private static final Pattern FEATURE_PATTERN = + Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]"); + + private static final String FEATURE_TYPE_FOLD = "fold"; + private static final String FEATURE_TYPE_HINGE = "hinge"; + + private Context mContext; + private SettingsObserver mSettingsObserver; + + final class SettingsObserver extends ContentObserver { + private final Uri mDevicePostureUri = + Settings.Global.getUriFor(DEVICE_POSTURE); + private final Uri mDisplayFeaturesUri = + Settings.Global.getUriFor(DISPLAY_FEATURES); + private final ContentResolver mResolver = mContext.getContentResolver(); + private boolean mRegisteredObservers; + + + private SettingsObserver() { + super(new Handler(Looper.getMainLooper())); + } + + private void registerObserversIfNeeded() { + if (mRegisteredObservers) { + return; + } + mRegisteredObservers = true; + mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendents */, + this /* ContentObserver */); + mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendents */, + this /* ContentObserver */); + } + + private void unregisterObserversIfNeeded() { + if (!mRegisteredObservers) { + return; + } + mRegisteredObservers = false; + mResolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri == null) { + return; + } + + if (mDevicePostureUri.equals(uri)) { + updateDevicePosture(); + return; + } + if (mDisplayFeaturesUri.equals(uri)) { + updateDisplayFeatures(); + return; + } + } + } + + SettingsExtensionImpl(Context context) { + mContext = context; + mSettingsObserver = new SettingsObserver(); + } + + private void updateDevicePosture() { + updateDeviceState(getDeviceState()); + } + + /** Update display features with values read from settings. */ + private void updateDisplayFeatures() { + for (IBinder windowToken : getWindowsListeningForLayoutChanges()) { + ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken); + updateWindowLayout(windowToken, newLayout); + } + } + + @NonNull + @Override + public ExtensionDeviceState getDeviceState() { + ContentResolver resolver = mContext.getContentResolver(); + int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE, + ExtensionDeviceState.POSTURE_UNKNOWN); + return new ExtensionDeviceState(posture); + } + + @NonNull + @Override + public ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) { + List<ExtensionDisplayFeature> displayFeatures = readDisplayFeatures(windowToken); + return new ExtensionWindowLayoutInfo(displayFeatures); + } + + private List<ExtensionDisplayFeature> readDisplayFeatures(IBinder windowToken) { + List<ExtensionDisplayFeature> features = new ArrayList<ExtensionDisplayFeature>(); + int displayId = getWindowDisplay(windowToken); + if (displayId != DEFAULT_DISPLAY) { + Log.w(TAG, "This sample doesn't support display features on secondary displays"); + return features; + } + + ContentResolver resolver = mContext.getContentResolver(); + final String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES); + if (isInMultiWindow(windowToken)) { + // It is recommended not to report any display features in multi-window mode, since it + // won't be possible to synchronize the display feature positions with window movement. + return features; + } + if (TextUtils.isEmpty(displayFeaturesString)) { + return features; + } + + String[] featureStrings = displayFeaturesString.split(";"); + for (String featureString : featureStrings) { + Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString); + if (!featureMatcher.matches()) { + Log.e(TAG, "Malformed feature description format: " + featureString); + continue; + } + try { + String featureType = featureMatcher.group(1); + int type; + switch (featureType) { + case FEATURE_TYPE_FOLD: + type = ExtensionDisplayFeature.TYPE_FOLD; + break; + case FEATURE_TYPE_HINGE: + type = ExtensionDisplayFeature.TYPE_HINGE; + break; + default: { + Log.e(TAG, "Malformed feature type: " + featureType); + continue; + } + } + + int left = Integer.parseInt(featureMatcher.group(2)); + int top = Integer.parseInt(featureMatcher.group(3)); + int right = Integer.parseInt(featureMatcher.group(4)); + int bottom = Integer.parseInt(featureMatcher.group(5)); + Rect featureRect = new Rect(left, top, right, bottom); + rotateRectToDisplayRotation(featureRect, displayId); + transformToWindowSpaceRect(featureRect, windowToken); + if (!featureRect.isEmpty()) { + ExtensionDisplayFeature feature = + new ExtensionDisplayFeature(featureRect, type); + features.add(feature); + } else { + Log.w(TAG, "Failed to adjust feature to window"); + } + } catch (NumberFormatException e) { + Log.e(TAG, "Malformed feature description: " + featureString); + } + } + return features; + } + + @Override + protected void onListenersChanged() { + if (mSettingsObserver == null) { + return; + } + + if (hasListeners()) { + mSettingsObserver.registerObserversIfNeeded(); + } else { + mSettingsObserver.unregisterObserversIfNeeded(); + } + } +} diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar Binary files differnew file mode 100644 index 000000000000..0ebbb86daf82 --- /dev/null +++ b/libs/WindowManager/Jetpack/window-extensions-release.aar diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java index 1ac4b4b29fbf..323bba3fdd71 100644 --- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java +++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java @@ -624,7 +624,7 @@ public class GnssMetrics { private void registerGnssStats() { mPullAtomCallback = new StatsPullAtomCallbackImpl(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( FrameworkStatsLog.GNSS_STATS, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), mPullAtomCallback); diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java index 7245aab41eec..e67ba5905aae 100644 --- a/media/java/android/media/AudioMetadata.java +++ b/media/java/android/media/AudioMetadata.java @@ -16,10 +16,18 @@ package android.media; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.Log; import android.util.Pair; +import java.lang.reflect.ParameterizedType; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; import java.util.Objects; @@ -30,6 +38,8 @@ import java.util.Set; * configuration and capability requests within the Audio Framework. */ public final class AudioMetadata { + private static final String TAG = "AudioMetadata"; + /** * Key interface for the map. * @@ -273,7 +283,7 @@ public final class AudioMetadata { * @hide */ @NonNull - public static <T> Key<T> createKey(String name, Class<T> type) { + public static <T> Key<T> createKey(@NonNull String name, @NonNull Class<T> type) { // Implementation specific. return new Key<T>() { private final String mName = name; @@ -296,6 +306,26 @@ public final class AudioMetadata { public boolean isFromFramework() { return true; } + + /** + * Return true if the name and the type of two objects are the same. + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Key)) { + return false; + } + Key<?> other = (Key<?>) obj; + return mName.equals(other.getName()) && mType.equals(other.getValueClass()); + } + + @Override + public int hashCode() { + return Objects.hash(mName, mType); + } }; } @@ -364,6 +394,27 @@ public final class AudioMetadata { return mHashMap.size(); } + /** + * Return true if the object is a BaseMap and the content from two BaseMap are the same. + * Note: Need to override the equals functions of Key<T> for HashMap comparison. + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof BaseMap)) { + return false; + } + BaseMap other = (BaseMap) obj; + return mHashMap.equals(other.mHashMap); + } + + @Override + public int hashCode() { + return Objects.hash(mHashMap); + } + /* * Implementation specific. * @@ -401,6 +452,445 @@ public final class AudioMetadata { new HashMap(); } + // The audio metadata object type index should be kept the same as + // the ones in audio_utils::metadata::metadata_types + private static final int AUDIO_METADATA_OBJ_TYPE_NONE = 0; + private static final int AUDIO_METADATA_OBJ_TYPE_INT = 1; + private static final int AUDIO_METADATA_OBJ_TYPE_LONG = 2; + private static final int AUDIO_METADATA_OBJ_TYPE_FLOAT = 3; + private static final int AUDIO_METADATA_OBJ_TYPE_DOUBLE = 4; + private static final int AUDIO_METADATA_OBJ_TYPE_STRING = 5; + // BaseMap is corresponding to audio_utils::metadata::Data + private static final int AUDIO_METADATA_OBJ_TYPE_BASEMAP = 6; + + private static final HashMap<Class, Integer> AUDIO_METADATA_OBJ_TYPES = new HashMap<>() {{ + put(Integer.class, AUDIO_METADATA_OBJ_TYPE_INT); + put(Long.class, AUDIO_METADATA_OBJ_TYPE_LONG); + put(Float.class, AUDIO_METADATA_OBJ_TYPE_FLOAT); + put(Double.class, AUDIO_METADATA_OBJ_TYPE_DOUBLE); + put(String.class, AUDIO_METADATA_OBJ_TYPE_STRING); + put(BaseMap.class, AUDIO_METADATA_OBJ_TYPE_BASEMAP); + }}; + + private static final Charset AUDIO_METADATA_CHARSET = StandardCharsets.UTF_8; + + /** + * An auto growing byte buffer + */ + private static class AutoGrowByteBuffer { + private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE; + private static final int LONG_BYTE_COUNT = Long.SIZE / Byte.SIZE; + private static final int FLOAT_BYTE_COUNT = Float.SIZE / Byte.SIZE; + private static final int DOUBLE_BYTE_COUNT = Double.SIZE / Byte.SIZE; + + private ByteBuffer mBuffer; + + AutoGrowByteBuffer() { + this(1024); + } + + AutoGrowByteBuffer(@IntRange(from = 0) int initialCapacity) { + mBuffer = ByteBuffer.allocateDirect(initialCapacity); + } + + public ByteBuffer getRawByteBuffer() { + // Slice the buffer from 0 to position. + int limit = mBuffer.limit(); + int position = mBuffer.position(); + mBuffer.limit(position); + mBuffer.position(0); + ByteBuffer buffer = mBuffer.slice(); + + // Restore position and limit. + mBuffer.limit(limit); + mBuffer.position(position); + return buffer; + } + + public ByteOrder order() { + return mBuffer.order(); + } + + public int position() { + return mBuffer.position(); + } + + public AutoGrowByteBuffer position(int newPosition) { + mBuffer.position(newPosition); + return this; + } + + public AutoGrowByteBuffer order(ByteOrder order) { + mBuffer.order(order); + return this; + } + + public AutoGrowByteBuffer putInt(int value) { + ensureCapacity(INTEGER_BYTE_COUNT); + mBuffer.putInt(value); + return this; + } + + public AutoGrowByteBuffer putLong(long value) { + ensureCapacity(LONG_BYTE_COUNT); + mBuffer.putLong(value); + return this; + } + + public AutoGrowByteBuffer putFloat(float value) { + ensureCapacity(FLOAT_BYTE_COUNT); + mBuffer.putFloat(value); + return this; + } + + public AutoGrowByteBuffer putDouble(double value) { + ensureCapacity(DOUBLE_BYTE_COUNT); + mBuffer.putDouble(value); + return this; + } + + public AutoGrowByteBuffer put(byte[] src) { + ensureCapacity(src.length); + mBuffer.put(src); + return this; + } + + /** + * Ensures capacity to append at least <code>count</code> values. + */ + private void ensureCapacity(@IntRange int count) { + if (mBuffer.remaining() < count) { + int newCapacity = mBuffer.position() + count; + if (newCapacity > Integer.MAX_VALUE >> 1) { + throw new IllegalStateException( + "Item memory requirements too large: " + newCapacity); + } + newCapacity <<= 1; + ByteBuffer buffer = ByteBuffer.allocateDirect(newCapacity); + buffer.order(mBuffer.order()); + + // Copy data from old buffer to new buffer + mBuffer.flip(); + buffer.put(mBuffer); + + // Set buffer to new buffer + mBuffer = buffer; + } + } + } + + /** + * @hide + * Describes a unpacking/packing contract of type {@code T} out of a {@link ByteBuffer} + * + * @param <T> the type being unpack + */ + private interface DataPackage<T> { + /** + * Read an item from a {@link ByteBuffer}. + * + * The parceling format is assumed the same as the one described in + * audio_utils::Metadata.h. Copied here as a reference. + * All values are native endian order. + * + * Datum = { (type_size_t) Type (the type index from type_as_value<T>.) + * (datum_size_t) Size (size of datum, including the size field) + * (byte string) Payload<Type> + * } + * + * Primitive types: + * Payload<Type> = { bytes in native endian order } + * + * Vector, Map, Container types: + * Payload<Type> = { (index_size_t) number of elements + * (byte string) Payload<Element_Type> * number + * } + * + * Pair container types: + * Payload<Type> = { (byte string) Payload<first>, + * (byte string) Payload<second> + * } + * + * @param buffer the byte buffer to read from + * @return an object, which types is given type for {@link DataPackage} + * @throws BufferUnderflowException when there is no enough data remaining + * in the buffer for unpacking. + */ + @Nullable + T unpack(ByteBuffer buffer); + + /** + * Pack the item into a byte array. This is the reversed way of unpacking. + * + * @param output is the stream to which to write the data + * @param obj the item to pack + * @return true if packing successfully. Otherwise, return false. + */ + boolean pack(AutoGrowByteBuffer output, T obj); + + /** + * Return what kind of data is contained in the package. + */ + default Class getMyType() { + return (Class) ((ParameterizedType) getClass().getGenericInterfaces()[0]) + .getActualTypeArguments()[0]; + } + } + + /***************************************************************************************** + * Following class are common {@link DataPackage} implementations, which include types + * that are defined in audio_utils::metadata::metadata_types + * + * For Java + * int32_t corresponds to Integer + * int64_t corresponds to Long + * float corresponds to Float + * double corresponds to Double + * std::string corresponds to String + * Data corresponds to BaseMap + * Datum corresponds to Object + ****************************************************************************************/ + + private static final HashMap<Integer, DataPackage<?>> DATA_PACKAGES = new HashMap<>() {{ + put(AUDIO_METADATA_OBJ_TYPE_INT, new DataPackage<Integer>() { + @Override + @Nullable + public Integer unpack(ByteBuffer buffer) { + return buffer.getInt(); + } + + @Override + public boolean pack(AutoGrowByteBuffer output, Integer obj) { + output.putInt(obj); + return true; + } + }); + put(AUDIO_METADATA_OBJ_TYPE_LONG, new DataPackage<Long>() { + @Override + @Nullable + public Long unpack(ByteBuffer buffer) { + return buffer.getLong(); + } + + @Override + public boolean pack(AutoGrowByteBuffer output, Long obj) { + output.putLong(obj); + return true; + } + }); + put(AUDIO_METADATA_OBJ_TYPE_FLOAT, new DataPackage<Float>() { + @Override + @Nullable + public Float unpack(ByteBuffer buffer) { + return buffer.getFloat(); + } + + @Override + public boolean pack(AutoGrowByteBuffer output, Float obj) { + output.putFloat(obj); + return true; + } + }); + put(AUDIO_METADATA_OBJ_TYPE_DOUBLE, new DataPackage<Double>() { + @Override + @Nullable + public Double unpack(ByteBuffer buffer) { + return buffer.getDouble(); + } + + @Override + public boolean pack(AutoGrowByteBuffer output, Double obj) { + output.putDouble(obj); + return true; + } + }); + put(AUDIO_METADATA_OBJ_TYPE_STRING, new DataPackage<String>() { + @Override + @Nullable + public String unpack(ByteBuffer buffer) { + int dataSize = buffer.getInt(); + if (buffer.position() + dataSize > buffer.limit()) { + return null; + } + byte[] valueArr = new byte[dataSize]; + buffer.get(valueArr); + String value = new String(valueArr, AUDIO_METADATA_CHARSET); + return value; + } + + /** + * This is a reversed operation of unpack. It is needed to write the String + * at bytes encoded with AUDIO_METADATA_CHARSET. There should be an integer + * value representing the length of the bytes written before the bytes. + */ + @Override + public boolean pack(AutoGrowByteBuffer output, String obj) { + byte[] valueArr = obj.getBytes(AUDIO_METADATA_CHARSET); + output.putInt(valueArr.length); + output.put(valueArr); + return true; + } + }); + put(AUDIO_METADATA_OBJ_TYPE_BASEMAP, new BaseMapPackage()); + }}; + // ObjectPackage is a special case that it is expected to unpack audio_utils::metadata::Datum, + // which contains data type and data size besides the payload for the data. + private static final ObjectPackage OBJECT_PACKAGE = new ObjectPackage(); + + private static class ObjectPackage implements DataPackage<Pair<Class, Object>> { + /** + * The {@link ObjectPackage} will unpack byte string for audio_utils::metadata::Datum. + * Since the Datum is a std::any, {@link Object} is used to carrying the data. The + * data type is stored in the data package header. In that case, a {@link Class} + * will also be returned to indicate the actual type for the object. + */ + @Override + @Nullable + public Pair<Class, Object> unpack(ByteBuffer buffer) { + int dataType = buffer.getInt(); + DataPackage dataPackage = DATA_PACKAGES.get(dataType); + if (dataPackage == null) { + Log.e(TAG, "Cannot find DataPackage for type:" + dataType); + return null; + } + int dataSize = buffer.getInt(); + int position = buffer.position(); + Object obj = dataPackage.unpack(buffer); + if (buffer.position() - position != dataSize) { + Log.e(TAG, "Broken data package"); + return null; + } + return new Pair<Class, Object>(dataPackage.getMyType(), obj); + } + + @Override + public boolean pack(AutoGrowByteBuffer output, Pair<Class, Object> obj) { + final Integer dataType = AUDIO_METADATA_OBJ_TYPES.get(obj.first); + if (dataType == null) { + Log.e(TAG, "Cannot find data type for " + obj.first); + return false; + } + DataPackage dataPackage = DATA_PACKAGES.get(dataType); + if (dataPackage == null) { + Log.e(TAG, "Cannot find DataPackage for type:" + dataType); + return false; + } + output.putInt(dataType); + int position = output.position(); // Keep current position. + output.putInt(0); // Keep a place for the size of payload. + int payloadIdx = output.position(); + if (!dataPackage.pack(output, obj.second)) { + Log.i(TAG, "Failed to pack object: " + obj.second); + return false; + } + // Put the actual payload size. + int currentPosition = output.position(); + output.position(position); + output.putInt(currentPosition - payloadIdx); + output.position(currentPosition); + return true; + } + } + + /** + * BaseMap will be corresponding to audio_utils::metadata::Data. + */ + private static class BaseMapPackage implements DataPackage<BaseMap> { + @Override + @Nullable + public BaseMap unpack(ByteBuffer buffer) { + BaseMap ret = new BaseMap(); + int mapSize = buffer.getInt(); + DataPackage<String> strDataPackage = + (DataPackage<String>) DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_STRING); + if (strDataPackage == null) { + Log.e(TAG, "Cannot find DataPackage for String"); + return null; + } + for (int i = 0; i < mapSize; i++) { + String key = strDataPackage.unpack(buffer); + if (key == null) { + Log.e(TAG, "Failed to unpack key for map"); + return null; + } + Pair<Class, Object> value = OBJECT_PACKAGE.unpack(buffer); + if (value == null) { + Log.e(TAG, "Failed to unpack value for map"); + return null; + } + ret.set(createKey(key, value.first), value.first.cast(value.second)); + } + return ret; + } + + @Override + public boolean pack(AutoGrowByteBuffer output, BaseMap obj) { + output.putInt(obj.size()); + DataPackage<String> strDataPackage = + (DataPackage<String>) DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_STRING); + if (strDataPackage == null) { + Log.e(TAG, "Cannot find DataPackage for String"); + return false; + } + for (Key<?> key : obj.keySet()) { + if (!strDataPackage.pack(output, key.getName())) { + Log.i(TAG, "Failed to pack key: " + key.getName()); + return false; + } + if (!OBJECT_PACKAGE.pack(output, new Pair<>(key.getValueClass(), obj.get(key)))) { + Log.i(TAG, "Failed to pack value: " + obj.get(key)); + return false; + } + } + return true; + } + } + + /** + * @hide + * Extract a {@link BaseMap} from a given {@link ByteBuffer} + * @param buffer is a byte string that contains information to unpack. + * @return a {@link BaseMap} object if extracting successfully from given byte buffer. + * Otherwise, returns {@code null}. + */ + @Nullable + public static BaseMap fromByteBuffer(ByteBuffer buffer) { + DataPackage dataPackage = DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_BASEMAP); + if (dataPackage == null) { + Log.e(TAG, "Cannot find DataPackage for BaseMap"); + return null; + } + try { + return (BaseMap) dataPackage.unpack(buffer); + } catch (BufferUnderflowException e) { + Log.e(TAG, "No enough data to unpack"); + } + return null; + } + + /** + * @hide + * Pack a {link BaseMap} to a {@link ByteBuffer} + * @param data is the object for packing + * @param order is the byte order + * @return a {@link ByteBuffer} if successfully packing the data. + * Otherwise, returns {@code null}; + */ + @Nullable + public static ByteBuffer toByteBuffer(BaseMap data, ByteOrder order) { + DataPackage dataPackage = DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_BASEMAP); + if (dataPackage == null) { + Log.e(TAG, "Cannot find DataPackage for BaseMap"); + return null; + } + AutoGrowByteBuffer output = new AutoGrowByteBuffer(); + output.order(order); + if (dataPackage.pack(output, data)) { + return output.getRawByteBuffer(); + } + return null; + } + // Delete the constructor as there is nothing to implement here. private AudioMetadata() {} } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 81275f6891ab..94d4fcc02eea 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -4050,8 +4050,15 @@ public class AudioTrack extends PlayerBase } if (what == NATIVE_EVENT_CODEC_FORMAT_CHANGE) { - track.mCodecFormatChangedListeners.notify( - 0 /* eventCode, unused */, (AudioMetadata.ReadMap) obj); + ByteBuffer buffer = (ByteBuffer) obj; + buffer.order(ByteOrder.nativeOrder()); + buffer.rewind(); + AudioMetadata.ReadMap audioMetaData = AudioMetadata.fromByteBuffer(buffer); + if (audioMetaData == null) { + Log.e(TAG, "Unable to get audio metadata from byte buffer"); + return; + } + track.mCodecFormatChangedListeners.notify(0 /* eventCode, unused */, audioMetaData); return; } diff --git a/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl b/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl index ab42d75bf14f..882caad0f49b 100644 --- a/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl +++ b/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl @@ -28,7 +28,6 @@ oneway interface IMediaRoute2ProviderServiceCallback { // TODO: Change it to updateRoutes? void updateState(in MediaRoute2ProviderInfo providerInfo); void notifySessionCreated(in RoutingSessionInfo sessionInfo, long requestId); - void notifySessionCreationFailed(long requestId); void notifySessionUpdated(in RoutingSessionInfo sessionInfo); void notifySessionReleased(in RoutingSessionInfo sessionInfo); void notifyRequestFailed(long requestId, int reason); diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index aa0eda1fdbb1..51696a4de2d4 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -233,6 +233,7 @@ public abstract class MediaRoute2ProviderService extends Service { String sessionId = sessionInfo.getId(); synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { + // TODO: Notify failure to the requester, and throw exception if needed. Log.w(TAG, "Ignoring duplicate session id."); return; } @@ -253,24 +254,6 @@ public abstract class MediaRoute2ProviderService extends Service { } /** - * Notifies clients of that the session could not be created. - * - * @param requestId id of the previous request to create the session provided in - * {@link #onCreateSession(long, String, String, Bundle)}. - * @see #onCreateSession(long, String, String, Bundle) - */ - public final void notifySessionCreationFailed(long requestId) { - if (mRemoteCallback == null) { - return; - } - try { - mRemoteCallback.notifySessionCreationFailed(requestId); - } catch (RemoteException ex) { - Log.w(TAG, "Failed to notify session creation failed."); - } - } - - /** * Notifies the existing session is updated. For example, when * {@link RoutingSessionInfo#getSelectedRoutes() selected routes} are changed. */ @@ -364,7 +347,7 @@ public abstract class MediaRoute2ProviderService extends Service { * {@link Bundle} which contains how to control the session. * <p> * If you can't create the session or want to reject the request, call - * {@link #notifySessionCreationFailed(long)} with the given {@code requestId}. + * {@link #notifyRequestFailed(long, int)} with the given {@code requestId}. * * @param requestId the id of this request * @param packageName the package name of the application that selected the route diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 734d78e54414..cf453284d39f 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -304,13 +304,9 @@ public class MediaRouter2Manager { * @see Callback#onTransferFailed(RoutingSessionInfo, MediaRoute2Info) */ public void transfer(@NonNull RoutingSessionInfo sessionInfo, - @Nullable MediaRoute2Info route) { + @NonNull MediaRoute2Info route) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); - - if (route == null) { - releaseSession(sessionInfo); - return; - } + Objects.requireNonNull(route, "route must not be null"); //TODO: Ignore unknown route. if (sessionInfo.getTransferableRoutes().contains(route.getId())) { @@ -334,10 +330,10 @@ public class MediaRouter2Manager { int requestId = mNextRequestId.getAndIncrement(); mMediaRouterService.requestCreateSessionWithManager( client, sessionInfo.getClientPackageName(), route, requestId); - releaseSession(sessionInfo); } catch (RemoteException ex) { Log.e(TAG, "Unable to select media route", ex); } + releaseSession(sessionInfo); } } diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java index ffb26fe49d8b..629cf1544bd1 100644 --- a/media/java/android/media/RoutingSessionInfo.java +++ b/media/java/android/media/RoutingSessionInfo.java @@ -22,6 +22,7 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.Log; import java.util.ArrayList; import java.util.Collections; @@ -73,10 +74,14 @@ public final class RoutingSessionInfo implements Parcelable { mClientPackageName = builder.mClientPackageName; mProviderId = builder.mProviderId; - mSelectedRoutes = Collections.unmodifiableList(builder.mSelectedRoutes); - mSelectableRoutes = Collections.unmodifiableList(builder.mSelectableRoutes); - mDeselectableRoutes = Collections.unmodifiableList(builder.mDeselectableRoutes); - mTransferableRoutes = Collections.unmodifiableList(builder.mTransferableRoutes); + mSelectedRoutes = Collections.unmodifiableList( + convertToUniqueRouteIds(builder.mSelectedRoutes)); + mSelectableRoutes = Collections.unmodifiableList( + convertToUniqueRouteIds(builder.mSelectableRoutes)); + mDeselectableRoutes = Collections.unmodifiableList( + convertToUniqueRouteIds(builder.mDeselectableRoutes)); + mTransferableRoutes = Collections.unmodifiableList( + convertToUniqueRouteIds(builder.mTransferableRoutes)); mVolumeHandling = builder.mVolumeHandling; mVolumeMax = builder.mVolumeMax; @@ -326,6 +331,24 @@ public final class RoutingSessionInfo implements Parcelable { return result.toString(); } + private List<String> convertToUniqueRouteIds(@NonNull List<String> routeIds) { + if (routeIds == null) { + Log.w(TAG, "routeIds is null. Returning an empty list"); + return Collections.emptyList(); + } + + // mProviderId can be null if not set. Return the original list for this case. + if (mProviderId == null) { + return routeIds; + } + + List<String> result = new ArrayList<>(); + for (String routeId : routeIds) { + result.add(MediaRouter2Utils.toUniqueId(mProviderId, routeId)); + } + return result; + } + /** * Builder class for {@link RoutingSessionInfo}. */ @@ -345,7 +368,6 @@ public final class RoutingSessionInfo implements Parcelable { Bundle mControlHints; boolean mIsSystemSession; - //TODO: Remove this. /** * Constructor for builder to create {@link RoutingSessionInfo}. * <p> @@ -392,6 +414,14 @@ public final class RoutingSessionInfo implements Parcelable { mDeselectableRoutes = new ArrayList<>(sessionInfo.mDeselectableRoutes); mTransferableRoutes = new ArrayList<>(sessionInfo.mTransferableRoutes); + if (mProviderId != null) { + // They must have unique IDs. + mSelectedRoutes.replaceAll(MediaRouter2Utils::getOriginalId); + mSelectableRoutes.replaceAll(MediaRouter2Utils::getOriginalId); + mDeselectableRoutes.replaceAll(MediaRouter2Utils::getOriginalId); + mTransferableRoutes.replaceAll(MediaRouter2Utils::getOriginalId); + } + mVolumeHandling = sessionInfo.mVolumeHandling; mVolumeMax = sessionInfo.mVolumeMax; mVolume = sessionInfo.mVolume; @@ -431,12 +461,6 @@ public final class RoutingSessionInfo implements Parcelable { throw new IllegalArgumentException("providerId must not be empty"); } mProviderId = providerId; - - mSelectedRoutes.replaceAll(routeId -> getUniqueId(mProviderId, routeId)); - mSelectableRoutes.replaceAll(routeId -> getUniqueId(mProviderId, routeId)); - mDeselectableRoutes.replaceAll(routeId -> getUniqueId(mProviderId, routeId)); - mTransferableRoutes.replaceAll(routeId -> getUniqueId(mProviderId, routeId)); - return this; } @@ -457,7 +481,7 @@ public final class RoutingSessionInfo implements Parcelable { if (TextUtils.isEmpty(routeId)) { throw new IllegalArgumentException("routeId must not be empty"); } - mSelectedRoutes.add(getUniqueId(mProviderId, routeId)); + mSelectedRoutes.add(routeId); return this; } @@ -469,7 +493,7 @@ public final class RoutingSessionInfo implements Parcelable { if (TextUtils.isEmpty(routeId)) { throw new IllegalArgumentException("routeId must not be empty"); } - mSelectedRoutes.remove(getUniqueId(mProviderId, routeId)); + mSelectedRoutes.remove(routeId); return this; } @@ -490,7 +514,7 @@ public final class RoutingSessionInfo implements Parcelable { if (TextUtils.isEmpty(routeId)) { throw new IllegalArgumentException("routeId must not be empty"); } - mSelectableRoutes.add(getUniqueId(mProviderId, routeId)); + mSelectableRoutes.add(routeId); return this; } @@ -502,7 +526,7 @@ public final class RoutingSessionInfo implements Parcelable { if (TextUtils.isEmpty(routeId)) { throw new IllegalArgumentException("routeId must not be empty"); } - mSelectableRoutes.remove(getUniqueId(mProviderId, routeId)); + mSelectableRoutes.remove(routeId); return this; } @@ -523,7 +547,7 @@ public final class RoutingSessionInfo implements Parcelable { if (TextUtils.isEmpty(routeId)) { throw new IllegalArgumentException("routeId must not be empty"); } - mDeselectableRoutes.add(getUniqueId(mProviderId, routeId)); + mDeselectableRoutes.add(routeId); return this; } @@ -535,7 +559,7 @@ public final class RoutingSessionInfo implements Parcelable { if (TextUtils.isEmpty(routeId)) { throw new IllegalArgumentException("routeId must not be empty"); } - mDeselectableRoutes.remove(getUniqueId(mProviderId, routeId)); + mDeselectableRoutes.remove(routeId); return this; } @@ -556,7 +580,7 @@ public final class RoutingSessionInfo implements Parcelable { if (TextUtils.isEmpty(routeId)) { throw new IllegalArgumentException("routeId must not be empty"); } - mTransferableRoutes.add(getUniqueId(mProviderId, routeId)); + mTransferableRoutes.add(routeId); return this; } @@ -568,7 +592,7 @@ public final class RoutingSessionInfo implements Parcelable { if (TextUtils.isEmpty(routeId)) { throw new IllegalArgumentException("routeId must not be empty"); } - mTransferableRoutes.remove(getUniqueId(mProviderId, routeId)); + mTransferableRoutes.remove(routeId); return this; } @@ -634,15 +658,4 @@ public final class RoutingSessionInfo implements Parcelable { return new RoutingSessionInfo(this); } } - - private static String getUniqueId(String providerId, String routeId) { - if (TextUtils.isEmpty(providerId)) { - return routeId; - } - String originalId = MediaRouter2Utils.getOriginalId(routeId); - if (originalId == null) { - originalId = routeId; - } - return MediaRouter2Utils.toUniqueId(providerId, originalId); - } } diff --git a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java index 064ab80efb5c..7b2949472396 100644 --- a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java @@ -34,6 +34,27 @@ import java.lang.annotation.RetentionPolicy; */ @SystemApi public class AlpFilterConfiguration extends FilterConfiguration { + /** + * IPv4 packet type. + */ + public static final int PACKET_TYPE_IPV4 = 0; + /** + * Compressed packet type. + */ + public static final int PACKET_TYPE_COMPRESSED = 2; + /** + * Signaling packet type. + */ + public static final int PACKET_TYPE_SIGNALING = 4; + /** + * Extension packet type. + */ + public static final int PACKET_TYPE_EXTENSION = 6; + /** + * MPEG-2 TS packet type. + */ + public static final int PACKET_TYPE_MPEG2_TS = 7; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "LENGTH_TYPE_", value = @@ -73,8 +94,9 @@ public class AlpFilterConfiguration extends FilterConfiguration { /** * Gets packet type. + * + * <p>The meaning of each packet type value is shown in ATSC A/330:2019 table 5.2. */ - @FilterConfiguration.PacketType public int getPacketType() { return mPacketType; } @@ -110,9 +132,11 @@ public class AlpFilterConfiguration extends FilterConfiguration { /** * Sets packet type. + * + * <p>The meaning of each packet type value is shown in ATSC A/330:2019 table 5.2. */ @NonNull - public Builder setPacketType(@FilterConfiguration.PacketType int packetType) { + public Builder setPacketType(int packetType) { mPacketType = packetType; return this; } diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index cfb943b10d17..52bdb595050e 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -45,6 +45,10 @@ public class Filter implements AutoCloseable { public @interface Type {} /** + * Undefined filter type. + */ + public static final int TYPE_UNDEFINED = 0; + /** * TS filter type. */ public static final int TYPE_TS = Constants.DemuxFilterMainType.TS; @@ -248,7 +252,6 @@ public class Filter implements AutoCloseable { /** * Gets the filter Id. */ - @Result public int getId() { return nativeGetId(); } @@ -273,6 +276,8 @@ public class Filter implements AutoCloseable { /** * Starts filtering data. * + * <p>Does nothing if the filter is already started. + * * @return result status of the operation. */ @Result @@ -284,6 +289,8 @@ public class Filter implements AutoCloseable { /** * Stops filtering data. * + * <p>Does nothing if the filter is stopped or not started. + * * @return result status of the operation. */ @Result @@ -312,14 +319,13 @@ public class Filter implements AutoCloseable { * @param size the maximum number of bytes to read. * @return the number of bytes read. */ - @Result public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) { size = Math.min(size, buffer.length - offset); return nativeRead(buffer, offset, size); } /** - * Releases the Filter instance. + * Stops filtering data and releases the Filter instance. */ @Override public void close() { diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java index c1d2275d3bdb..a8c9356b570a 100644 --- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java @@ -16,14 +16,10 @@ package android.media.tv.tuner.filter; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * Filter configuration used to configure filters. * @@ -32,26 +28,6 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public abstract class FilterConfiguration { - /** @hide */ - @IntDef(prefix = "PACKET_TYPE_", value = - {PACKET_TYPE_IPV4, PACKET_TYPE_COMPRESSED, PACKET_TYPE_SIGNALING}) - @Retention(RetentionPolicy.SOURCE) - public @interface PacketType {} - - /** - * IP v4 packet type. - */ - public static final int PACKET_TYPE_IPV4 = 0; - /** - * Compressed packet type. - */ - public static final int PACKET_TYPE_COMPRESSED = 2; - /** - * Signaling packet type. - */ - public static final int PACKET_TYPE_SIGNALING = 4; - - @Nullable /* package */ final Settings mSettings; diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java index 3d83a74a500c..ac4fc8313460 100644 --- a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java @@ -29,6 +29,27 @@ import android.media.tv.tuner.TunerUtils; */ @SystemApi public class TlvFilterConfiguration extends FilterConfiguration { + /** + * IPv4 packet type. + */ + public static final int PACKET_TYPE_IPV4 = 0x01; + /** + * IPv6 packet type. + */ + public static final int PACKET_TYPE_IPV6 = 0x02; + /** + * Compressed packet type. + */ + public static final int PACKET_TYPE_COMPRESSED = 0x03; + /** + * Signaling packet type. + */ + public static final int PACKET_TYPE_SIGNALING = 0xFE; + /** + * NULL packet type. + */ + public static final int PACKET_TYPE_NULL = 0xFF; + private final int mPacketType; private final boolean mIsCompressedIpPacket; private final boolean mPassthrough; @@ -48,8 +69,9 @@ public class TlvFilterConfiguration extends FilterConfiguration { /** * Gets packet type. + * + * <p>The description of each packet type value is shown in ITU-R BT.1869 table 2. */ - @FilterConfiguration.PacketType public int getPacketType() { return mPacketType; } @@ -96,9 +118,11 @@ public class TlvFilterConfiguration extends FilterConfiguration { /** * Sets packet type. + * + * <p>The description of each packet type value is shown in ITU-R BT.1869 table 2. */ @NonNull - public Builder setPacketType(@FilterConfiguration.PacketType int packetType) { + public Builder setPacketType(int packetType) { mPacketType = packetType; return this; } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java index 2230e2585656..31f240d87f94 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java @@ -70,7 +70,7 @@ public class RoutingSessionInfoTest { RoutingSessionInfo sessionInfoWithOtherProviderId = new RoutingSessionInfo.Builder(sessionInfoWithProviderId) - .setProviderId(TEST_OTHER_PROVIDER_ID).build(); + .setProviderId(TEST_OTHER_PROVIDER_ID).build(); assertNotEquals(sessionInfoWithOtherProviderId.getSelectedRoutes(), sessionInfoWithProviderId.getSelectedRoutes()); diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java index 0e7c7fc499b4..22c5bce5b381 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java @@ -216,8 +216,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService @Nullable Bundle sessionHints) { MediaRoute2Info route = mRoutes.get(routeId); if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) { - // Tell the router that session cannot be created by passing null as sessionInfo. - notifySessionCreationFailed(requestId); + notifyRequestFailed(requestId, REASON_UNKNOWN_ERROR); return; } maybeDeselectRoute(routeId); diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml index 2fe9d21b0489..55207b335aed 100644 --- a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml +++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml @@ -16,6 +16,7 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/fullscreen_user_switcher" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> @@ -24,22 +25,26 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_alignParentTop="true" android:orientation="vertical"> - <include - layout="@layout/car_status_bar_header" - android:layout_alignParentTop="true" - android:theme="@android:style/Theme"/> + <!-- TODO(b/150302361): Status bar is commented out since a top inset is being added which causes it to be displayed below the top of the screen. --> + <!-- <include + layout="@layout/car_status_bar_header" + android:layout_alignParentTop="true" + android:theme="@android:style/Theme"/>--> + <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> - <com.android.systemui.statusbar.car.UserGridRecyclerView + <com.android.systemui.car.userswitcher.UserGridRecyclerView android:id="@+id/user_grid" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:layout_marginTop="@dimen/car_user_switcher_margin_top"/> + android:layout_gravity="center_vertical"/> + <!-- TODO(b/150302361): Re-add marginTop once status bar has been added back. --> + <!-- android:layout_marginTop="@dimen/car_user_switcher_margin_top"/>--> </FrameLayout> </LinearLayout> diff --git a/packages/CarSystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml index 9c598d71bd22..0c6f322ca261 100644 --- a/packages/CarSystemUI/res/layout/car_qs_panel.xml +++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml @@ -33,7 +33,7 @@ android:layout_width="match_parent" android:layout_height="@dimen/car_user_switcher_container_height"> - <com.android.systemui.statusbar.car.UserGridRecyclerView + <com.android.systemui.car.userswitcher.UserGridRecyclerView android:id="@+id/user_grid" android:layout_width="match_parent" android:layout_height="match_parent"/> diff --git a/packages/CarSystemUI/res/layout/sysui_primary_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml index cc36e87eb480..cc36e87eb480 100644 --- a/packages/CarSystemUI/res/layout/sysui_primary_window.xml +++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index 825b2813d47d..aaa65de2fa1d 100644 --- a/packages/CarSystemUI/res/values/config.xml +++ b/packages/CarSystemUI/res/values/config.xml @@ -58,6 +58,11 @@ to a constant alpha percent value using the initial alpha. --> <integer name="config_finalNotificationBackgroundAlpha">100</integer> + <!-- Car System UI's OverlayViewsMediator--> + <string-array name="config_carSystemUIOverlayViewsMediators" translatable="false"> + <item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item> + </string-array> + <!-- SystemUI Services: The classes of the stuff to start. --> <string-array name="config_systemUIServiceComponents" translatable="false"> <item>com.android.systemui.util.NotificationChannels</item> @@ -85,5 +90,6 @@ <item>com.android.systemui.navigationbar.car.CarNavigationBar</item> <item>com.android.systemui.toast.ToastUI</item> <item>com.android.systemui.voicerecognition.car.ConnectedDeviceVoiceRecognitionNotifier</item> + <item>com.android.systemui.window.SystemUIOverlayWindowManager</item> </string-array> </resources> diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java index 8f9d7ed8c9b7..c01088108a9b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java @@ -39,6 +39,8 @@ import com.android.systemui.toast.ToastUI; import com.android.systemui.util.leak.GarbageMonitor; import com.android.systemui.voicerecognition.car.ConnectedDeviceVoiceRecognitionNotifier; import com.android.systemui.volume.VolumeUI; +import com.android.systemui.window.OverlayWindowModule; +import com.android.systemui.window.SystemUIOverlayWindowManager; import dagger.Binds; import dagger.Module; @@ -47,7 +49,7 @@ import dagger.multibindings.IntoMap; /** Binder for car specific {@link SystemUI} modules. */ @Module(includes = {RecentsModule.class, CarStatusBarModule.class, NotificationsModule.class, - BubbleModule.class, KeyguardModule.class}) + BubbleModule.class, KeyguardModule.class, OverlayWindowModule.class}) public abstract class CarSystemUIBinder { /** Inject into AuthController. */ @Binds @@ -182,4 +184,10 @@ public abstract class CarSystemUIBinder { @ClassKey(ConnectedDeviceVoiceRecognitionNotifier.class) public abstract SystemUI bindConnectedDeviceVoiceRecognitionNotifier( ConnectedDeviceVoiceRecognitionNotifier sysui); + + /** Inject into SystemUIOverlayWindowManager. */ + @Binds + @IntoMap + @ClassKey(SystemUIOverlayWindowManager.class) + public abstract SystemUI bindSystemUIPrimaryWindowManager(SystemUIOverlayWindowManager sysui); } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarTrustAgentUnlockDialogHelper.java index 07dbd667acf4..597716569e9b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarTrustAgentUnlockDialogHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.car; +package com.android.systemui.car.userswitcher; import android.app.admin.DevicePolicyManager; import android.bluetooth.BluetoothAdapter; @@ -56,8 +56,8 @@ class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{ private final UserManager mUserManager; private final WindowManager.LayoutParams mParams; /** - * Not using Dialog because context passed from {@link FullscreenUserSwitcher} is not an - * activity. + * Not using Dialog because context passed from {@link FullscreenUserSwitcherViewMediator} + * is not an activity. */ private final View mUnlockDialogLayout; private final TextView mUnlockingText; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java new file mode 100644 index 000000000000..45ceb6d1551f --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java @@ -0,0 +1,124 @@ +/* + * 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.car.userswitcher; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.content.Context; +import android.content.res.Resources; +import android.view.View; + +import androidx.recyclerview.widget.GridLayoutManager; + +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.window.OverlayViewController; +import com.android.systemui.window.OverlayViewGlobalStateController; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Controller for {@link R.layout#car_fullscreen_user_switcher}. + */ +@Singleton +public class FullScreenUserSwitcherViewController extends OverlayViewController { + private final Context mContext; + private final Resources mResources; + private final int mShortAnimationDuration; + private UserGridRecyclerView mUserGridView; + private UserGridRecyclerView.UserSelectionListener mUserSelectionListener; + + @Inject + public FullScreenUserSwitcherViewController( + Context context, + @Main Resources resources, + OverlayViewGlobalStateController overlayViewGlobalStateController) { + super(R.id.fullscreen_user_switcher_stub, overlayViewGlobalStateController); + mContext = context; + mResources = resources; + mShortAnimationDuration = mResources.getInteger(android.R.integer.config_shortAnimTime); + } + + @Override + protected void onFinishInflate() { + // Initialize user grid. + mUserGridView = getLayout().findViewById(R.id.user_grid); + GridLayoutManager layoutManager = new GridLayoutManager(mContext, + mResources.getInteger(R.integer.user_fullscreen_switcher_num_col)); + mUserGridView.setLayoutManager(layoutManager); + mUserGridView.buildAdapter(); + mUserGridView.setUserSelectionListener(mUserSelectionListener); + } + + @Override + protected void showInternal() { + getLayout().setVisibility(View.VISIBLE); + } + + @Override + protected void hideInternal() { + // Switching is about to happen, since it takes time, fade out the switcher gradually. + fadeOut(); + } + + private void fadeOut() { + mUserGridView.animate() + .alpha(0.0f) + .setDuration(mShortAnimationDuration) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + getLayout().setVisibility(View.GONE); + mUserGridView.setAlpha(1.0f); + } + }); + + } + + /** + * Invalidate underlying view. + */ + void invalidate() { + if (getLayout() == null) { + // layout hasn't been inflated. + return; + } + + getLayout().invalidate(); + } + + /** + * Set {@link UserGridRecyclerView.UserSelectionListener}. + */ + void setUserGridSelectionListener( + UserGridRecyclerView.UserSelectionListener userGridSelectionListener) { + mUserSelectionListener = userGridSelectionListener; + } + + /** + * Returns {@code true} when layout is visible. + */ + boolean isVisible() { + if (getLayout() == null) { + // layout hasn't been inflated. + return false; + } + + return getLayout().getVisibility() == View.VISIBLE; + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java new file mode 100644 index 000000000000..627729768e88 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java @@ -0,0 +1,291 @@ +/* + * 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.car.userswitcher; + +import android.car.Car; +import android.car.trust.CarTrustAgentEnrollmentManager; +import android.car.userlib.CarUserManagerHelper; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.os.Handler; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; + +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.R; +import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.car.CarStatusBar; +import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager; +import com.android.systemui.window.OverlayViewMediator; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Manages the fullscreen user switcher and it's interactions with the keyguard. + */ +@Singleton +public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator { + private static final String TAG = FullscreenUserSwitcherViewMediator.class.getSimpleName(); + + private final Context mContext; + private final UserManager mUserManager; + private final CarServiceProvider mCarServiceProvider; + private final CarTrustAgentUnlockDialogHelper mUnlockDialogHelper; + private final CarStatusBarKeyguardViewManager mCarStatusBarKeyguardViewManager; + private final Handler mMainHandler; + private final StatusBarStateController mStatusBarStateController; + private final FullScreenUserSwitcherViewController mFullScreenUserSwitcherViewController; + private final ScreenLifecycle mScreenLifecycle; + private final CarStatusBar mCarStatusBar; + private final boolean mIsUserSwitcherEnabled; + private final CarUserManagerHelper mCarUserManagerHelper; + + private CarTrustAgentEnrollmentManager mEnrollmentManager; + private UserGridRecyclerView.UserRecord mSelectedUser; + private final CarTrustAgentUnlockDialogHelper.OnHideListener mOnHideListener = + dismissUserSwitcher -> { + if (dismissUserSwitcher) { + dismissUserSwitcher(); + } else { + // Re-draw the parent view, otherwise the unlock dialog will not be removed + // from the screen immediately. + invalidateFullscreenUserSwitcherView(); + } + }; + private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible."); + } + showDialogForInitialUser(); + mContext.unregisterReceiver(mUserUnlockReceiver); + } + }; + + @Inject + public FullscreenUserSwitcherViewMediator( + Context context, + @Main Resources resources, + @Main Handler mainHandler, + UserManager userManager, + CarServiceProvider carServiceProvider, + CarTrustAgentUnlockDialogHelper carTrustAgentUnlockDialogHelper, + CarStatusBarKeyguardViewManager carStatusBarKeyguardViewManager, + CarStatusBar carStatusBar, + StatusBarStateController statusBarStateController, + FullScreenUserSwitcherViewController fullScreenUserSwitcherViewController, + ScreenLifecycle screenLifecycle) { + mContext = context; + + mIsUserSwitcherEnabled = resources.getBoolean(R.bool.config_enableFullscreenUserSwitcher); + + mMainHandler = mainHandler; + mUserManager = userManager; + + mCarServiceProvider = carServiceProvider; + mCarServiceProvider.addListener( + car -> mEnrollmentManager = (CarTrustAgentEnrollmentManager) car.getCarManager( + Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE)); + + mUnlockDialogHelper = carTrustAgentUnlockDialogHelper; + mCarStatusBarKeyguardViewManager = carStatusBarKeyguardViewManager; + mCarStatusBar = carStatusBar; + mStatusBarStateController = statusBarStateController; + mFullScreenUserSwitcherViewController = fullScreenUserSwitcherViewController; + mScreenLifecycle = screenLifecycle; + + mCarUserManagerHelper = new CarUserManagerHelper(mContext); + } + + @Override + public void registerListeners() { + registerUserSwitcherShowListeners(); + registerUserSwitcherHideListeners(); + registerHideKeyguardListeners(); + + if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) { + // User0 is unlocked, switched to the initial user + showDialogForInitialUser(); + } else { + // listen to USER_UNLOCKED + mContext.registerReceiverAsUser(mUserUnlockReceiver, + UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM), + new IntentFilter(Intent.ACTION_USER_UNLOCKED), + /* broadcastPermission= */ null, + /* scheduler= */ null); + } + } + + private void registerUserSwitcherShowListeners() { + mCarStatusBarKeyguardViewManager.addOnKeyguardCancelClickedListener(this::show); + } + + private void registerUserSwitcherHideListeners() { + mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + if (newState == StatusBarState.FULLSCREEN_USER_SWITCHER) { + return; + } + hide(); + } + }); + } + + private void registerHideKeyguardListeners() { + mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) { + return; + } + dismissKeyguardWhenUserSwitcherNotDisplayed(newState); + } + }); + + mScreenLifecycle.addObserver(new ScreenLifecycle.Observer() { + @Override + public void onScreenTurnedOn() { + dismissKeyguardWhenUserSwitcherNotDisplayed(mStatusBarStateController.getState()); + } + }); + + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!intent.getAction().equals(Intent.ACTION_USER_SWITCHED)) { + return; + } + + // Try to dismiss the keyguard after every user switch. + dismissKeyguardWhenUserSwitcherNotDisplayed(mStatusBarStateController.getState()); + } + }, new IntentFilter(Intent.ACTION_USER_SWITCHED)); + } + + @Override + public void setupOverlayContentViewControllers() { + mFullScreenUserSwitcherViewController.setUserGridSelectionListener(this::onUserSelected); + } + + /** + * Every time user clicks on an item in the switcher, if the clicked user has no trusted + * device, we hide the switcher, either gradually or immediately. + * If the user has trusted device, we show an unlock dialog to notify user the unlock + * state. + * When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher. + * We dismiss the entire keyguard when we hide the switcher if user clicked on the + * foreground user (user we're already logged in as). + */ + private void onUserSelected(UserGridRecyclerView.UserRecord record) { + mSelectedUser = record; + if (record.mInfo != null) { + if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) { + mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener); + return; + } + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id); + } + } + dismissUserSwitcher(); + } + + // We automatically dismiss keyguard unless user switcher is being shown above the keyguard. + private void dismissKeyguardWhenUserSwitcherNotDisplayed(int state) { + if (!mIsUserSwitcherEnabled) { + return; // Not using the full screen user switcher. + } + + if (state == StatusBarState.FULLSCREEN_USER_SWITCHER + && !mFullScreenUserSwitcherViewController.isVisible()) { + // Current execution path continues to set state after this, thus we deffer the + // dismissal to the next execution cycle. + + // Dismiss the keyguard if switcher is not visible. + // TODO(b/150402329): Remove once keyguard is implemented using Overlay Window + // abstractions. + mMainHandler.post(mCarStatusBar::dismissKeyguard); + } + } + + private boolean hasScreenLock(int uid) { + LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); + return lockPatternUtils.getCredentialTypeForUser(uid) + != LockPatternUtils.CREDENTIAL_TYPE_NONE; + } + + private boolean hasTrustedDevice(int uid) { + if (mEnrollmentManager == null) { // car service not ready, so it cannot be available. + return false; + } + return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty(); + } + + private void dismissUserSwitcher() { + if (mSelectedUser == null) { + Log.e(TAG, "Request to dismiss user switcher, but no user selected"); + return; + } + if (mSelectedUser.mType == UserGridRecyclerView.UserRecord.FOREGROUND_USER) { + hide(); + mCarStatusBar.dismissKeyguard(); + return; + } + hide(); + } + + private void showDialogForInitialUser() { + int initialUser = mCarUserManagerHelper.getInitialUser(); + UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser); + mSelectedUser = new UserGridRecyclerView.UserRecord(initialUserInfo, + UserGridRecyclerView.UserRecord.FOREGROUND_USER); + + // If the initial user has screen lock and trusted device, display the unlock dialog on the + // keyguard. + if (hasScreenLock(initialUser) && hasTrustedDevice(initialUser)) { + mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser, + mOnHideListener); + } else { + // If no trusted device, dismiss the keyguard. + dismissUserSwitcher(); + } + } + + private void invalidateFullscreenUserSwitcherView() { + mFullScreenUserSwitcherViewController.invalidate(); + } + + private void hide() { + mFullScreenUserSwitcherViewController.stop(); + } + + private void show() { + mFullScreenUserSwitcherViewController.start(); + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java index 7dd3be4b2c11..58add179886c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.car; +package com.android.systemui.car.userswitcher; import static android.content.DialogInterface.BUTTON_NEGATIVE; import static android.content.DialogInterface.BUTTON_POSITIVE; @@ -45,7 +45,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; -import android.view.WindowInsets; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; @@ -114,8 +113,6 @@ public class UserGridRecyclerView extends RecyclerView { /** * Initializes the adapter that populates the grid layout - * - * @return the adapter */ public void buildAdapter() { List<UserRecord> userRecords = createUserRecords(getUsersForUserGrid()); @@ -236,10 +233,16 @@ public class UserGridRecyclerView extends RecyclerView { mNewUserName = mRes.getString(R.string.car_new_user); } + /** + * Clears list of user records. + */ public void clearUsers() { mUsers.clear(); } + /** + * Updates list of user records. + */ public void updateUsers(List<UserRecord> users) { mUsers = users; } @@ -483,6 +486,10 @@ public class UserGridRecyclerView extends RecyclerView { return mUsers.size(); } + /** + * An extension of {@link RecyclerView.ViewHolder} that also houses the user name and the + * user avatar. + */ public class UserAdapterViewHolder extends RecyclerView.ViewHolder { public ImageView mUserAvatarImageView; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserIconProvider.java index 9018290f4955..dc5953e38ccb 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserIconProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.car; +package com.android.systemui.car.userswitcher; import android.annotation.UserIdInt; import android.content.Context; diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java index a56c4eda44da..67e9da429c36 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java +++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java @@ -71,6 +71,30 @@ public class CarNavigationBarController { mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar); } + /** + * Hides all navigation bars. + */ + public void hideBars() { + if (mTopView != null) { + mTopView.setVisibility(View.GONE); + } + setBottomWindowVisibility(View.GONE); + setLeftWindowVisibility(View.GONE); + setRightWindowVisibility(View.GONE); + } + + /** + * Shows all navigation bars. + */ + public void showBars() { + if (mTopView != null) { + mTopView.setVisibility(View.VISIBLE); + } + setBottomWindowVisibility(View.VISIBLE); + setLeftWindowVisibility(View.VISIBLE); + setRightWindowVisibility(View.VISIBLE); + } + /** Connect to hvac service. */ public void connectToHvac() { mHvacControllerLazy.get().connectToCarService(); diff --git a/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java index f9cfafa5c471..31965c5fc022 100644 --- a/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java +++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java @@ -35,9 +35,9 @@ import androidx.annotation.VisibleForTesting; import androidx.recyclerview.widget.GridLayoutManager; import com.android.systemui.R; +import com.android.systemui.car.userswitcher.UserGridRecyclerView; import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.QSFooter; -import com.android.systemui.statusbar.car.UserGridRecyclerView; import java.util.ArrayList; import java.util.List; 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 0374a5cef0ae..83248f4d867b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -67,7 +67,6 @@ import com.android.systemui.bubbles.BubbleController; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarDeviceProvisionedListener; import com.android.systemui.car.CarServiceProvider; -import com.android.systemui.car.SystemUIPrimaryWindowController; import com.android.systemui.classifier.FalsingLog; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -95,7 +94,6 @@ import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; @@ -181,14 +179,13 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private Drawable mNotificationPanelBackground; private final Object mQueueLock = new Object(); - private final SystemUIPrimaryWindowController mSystemUIPrimaryWindowController; private final CarNavigationBarController mCarNavigationBarController; private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; private final Lazy<PowerManagerHelper> mPowerManagerHelperLazy; - private final FullscreenUserSwitcher mFullscreenUserSwitcher; private final ShadeController mShadeController; private final CarServiceProvider mCarServiceProvider; private final CarDeviceProvisionedController mCarDeviceProvisionedController; + private final ScreenLifecycle mScreenLifecycle; private boolean mDeviceIsSetUpForUser = true; private boolean mIsUserSetupInProgress = false; @@ -196,7 +193,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private FlingAnimationUtils mFlingAnimationUtils; private NotificationDataManager mNotificationDataManager; private NotificationClickHandlerFactory mNotificationClickHandlerFactory; - private ScreenLifecycle mScreenLifecycle; // The container for the notifications. private CarNotificationView mNotificationView; @@ -333,8 +329,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt /* Car Settings injected components. */ CarServiceProvider carServiceProvider, Lazy<PowerManagerHelper> powerManagerHelperLazy, - FullscreenUserSwitcher fullscreenUserSwitcher, - SystemUIPrimaryWindowController systemUIPrimaryWindowController, CarNavigationBarController carNavigationBarController, FlingAnimationUtils.Builder flingAnimationUtilsBuilder) { super( @@ -423,10 +417,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mShadeController = shadeController; mCarServiceProvider = carServiceProvider; mPowerManagerHelperLazy = powerManagerHelperLazy; - mFullscreenUserSwitcher = fullscreenUserSwitcher; - mSystemUIPrimaryWindowController = systemUIPrimaryWindowController; mCarNavigationBarController = carNavigationBarController; mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder; + mScreenLifecycle = screenLifecycle; } @Override @@ -434,18 +427,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup(); mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress(); - // Need to initialize screen lifecycle before calling super.start - before switcher is - // created. - mScreenLifecycle = Dependency.get(ScreenLifecycle.class); - mScreenLifecycle.addObserver(mScreenObserver); - - // TODO: Remove the setup of user switcher from Car Status Bar. - mSystemUIPrimaryWindowController.attach(); - mFullscreenUserSwitcher.setStatusBar(this); - mFullscreenUserSwitcher.setContainer( - mSystemUIPrimaryWindowController.getBaseLayout().findViewById( - R.id.fullscreen_user_switcher_stub)); - // Notification bar related setup. mInitialBackgroundAlpha = (float) mContext.getResources().getInteger( R.integer.config_initialNotificationBackgroundAlpha) / 100; @@ -965,49 +946,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } } - @Override - public void setLockscreenUser(int newUserId) { - super.setLockscreenUser(newUserId); - // Try to dismiss the keyguard after every user switch. - dismissKeyguardWhenUserSwitcherNotDisplayed(); - } - - @Override - public void onStateChanged(int newState) { - super.onStateChanged(newState); - - if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) { - mFullscreenUserSwitcher.hide(); - } else { - dismissKeyguardWhenUserSwitcherNotDisplayed(); - } - } - - final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { - @Override - public void onScreenTurnedOn() { - dismissKeyguardWhenUserSwitcherNotDisplayed(); - } - }; - - // We automatically dismiss keyguard unless user switcher is being shown on the keyguard. - private void dismissKeyguardWhenUserSwitcherNotDisplayed() { - if (!mUserSwitcherController.useFullscreenUserSwitcher()) { - return; // Not using the full screen user switcher. - } - - if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER - && !mFullscreenUserSwitcher.isVisible()) { - // Current execution path continues to set state after this, thus we deffer the - // dismissal to the next execution cycle. - postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible. - } - } - - public void postDismissKeyguard() { - mHandler.post(this::dismissKeyguard); - } - /** * Dismisses the keyguard and shows bouncer if authentication is necessary. */ diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java index 59f9f94c4d05..e1c051f5012d 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java @@ -33,6 +33,9 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import java.util.HashSet; +import java.util.Set; + import javax.inject.Inject; import javax.inject.Singleton; @@ -42,7 +45,7 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage protected boolean mShouldHideNavBar; private final CarNavigationBarController mCarNavigationBarController; - private final FullscreenUserSwitcher mFullscreenUserSwitcher; + private Set<OnKeyguardCancelClickedListener> mKeygaurdCancelClickedListenerSet; @Inject public CarStatusBarKeyguardViewManager(Context context, @@ -56,8 +59,7 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage NotificationShadeWindowController notificationShadeWindowController, KeyguardStateController keyguardStateController, NotificationMediaManager notificationMediaManager, - CarNavigationBarController carNavigationBarController, - FullscreenUserSwitcher fullscreenUserSwitcher) { + CarNavigationBarController carNavigationBarController) { super(context, callback, lockPatternUtils, sysuiStatusBarStateController, configurationController, keyguardUpdateMonitor, navigationModeController, dockManager, notificationShadeWindowController, keyguardStateController, @@ -65,7 +67,7 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage mShouldHideNavBar = context.getResources() .getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown); mCarNavigationBarController = carNavigationBarController; - mFullscreenUserSwitcher = fullscreenUserSwitcher; + mKeygaurdCancelClickedListenerSet = new HashSet<>(); } @Override @@ -95,7 +97,7 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage */ @Override public void onCancelClicked() { - mFullscreenUserSwitcher.show(); + mKeygaurdCancelClickedListenerSet.forEach(OnKeyguardCancelClickedListener::onCancelClicked); } /** @@ -105,4 +107,31 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage */ @Override public void onDensityOrFontScaleChanged() { } + + /** + * Add listener for keyguard cancel clicked. + */ + public void addOnKeyguardCancelClickedListener( + OnKeyguardCancelClickedListener keyguardCancelClickedListener) { + mKeygaurdCancelClickedListenerSet.add(keyguardCancelClickedListener); + } + + /** + * Remove listener for keyguard cancel clicked. + */ + public void removeOnKeyguardCancelClickedListener( + OnKeyguardCancelClickedListener keyguardCancelClickedListener) { + mKeygaurdCancelClickedListenerSet.remove(keyguardCancelClickedListener); + } + + + /** + * Defines a callback for keyguard cancel button clicked listeners. + */ + public interface OnKeyguardCancelClickedListener { + /** + * Called when keyguard cancel button is clicked. + */ + void onCancelClicked(); + } } 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 07c42e17d006..aea4bd497527 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -31,7 +31,6 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.car.CarServiceProvider; -import com.android.systemui.car.SystemUIPrimaryWindowController; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.keyguard.DismissCallbackRegistry; @@ -205,8 +204,6 @@ public class CarStatusBarModule { StatusBarTouchableRegionManager statusBarTouchableRegionManager, CarServiceProvider carServiceProvider, Lazy<PowerManagerHelper> powerManagerHelperLazy, - FullscreenUserSwitcher fullscreenUserSwitcher, - SystemUIPrimaryWindowController systemUIPrimaryWindowController, CarNavigationBarController carNavigationBarController, FlingAnimationUtils.Builder flingAnimationUtilsBuilder) { return new CarStatusBar( @@ -288,8 +285,6 @@ public class CarStatusBarModule { statusBarTouchableRegionManager, carServiceProvider, powerManagerHelperLazy, - fullscreenUserSwitcher, - systemUIPrimaryWindowController, carNavigationBarController, flingAnimationUtilsBuilder); } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java deleted file mode 100644 index 3cd66c232717..000000000000 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java +++ /dev/null @@ -1,271 +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.statusbar.car; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.car.Car; -import android.car.trust.CarTrustAgentEnrollmentManager; -import android.car.userlib.CarUserManagerHelper; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.UserInfo; -import android.content.res.Resources; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.Log; -import android.view.View; -import android.view.ViewStub; - -import androidx.recyclerview.widget.GridLayoutManager; - -import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.R; -import com.android.systemui.car.CarServiceProvider; -import com.android.systemui.car.SystemUIPrimaryWindowController; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.statusbar.car.CarTrustAgentUnlockDialogHelper.OnHideListener; -import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * Manages the fullscreen user switcher. - */ -@Singleton -public class FullscreenUserSwitcher { - private static final String TAG = FullscreenUserSwitcher.class.getSimpleName(); - private final Context mContext; - private final Resources mResources; - private final UserManager mUserManager; - private final CarServiceProvider mCarServiceProvider; - private final CarTrustAgentUnlockDialogHelper mUnlockDialogHelper; - private final SystemUIPrimaryWindowController mSystemUIPrimaryWindowController; - private CarStatusBar mCarStatusBar; - private final int mShortAnimDuration; - - private View mParent; - private UserGridRecyclerView mUserGridView; - private CarTrustAgentEnrollmentManager mEnrollmentManager; - private UserGridRecyclerView.UserRecord mSelectedUser; - private CarUserManagerHelper mCarUserManagerHelper; - private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible."); - } - showDialogForInitialUser(); - mContext.unregisterReceiver(mUserUnlockReceiver); - } - }; - - @Inject - public FullscreenUserSwitcher( - Context context, - @Main Resources resources, - UserManager userManager, - CarServiceProvider carServiceProvider, - CarTrustAgentUnlockDialogHelper carTrustAgentUnlockDialogHelper, - SystemUIPrimaryWindowController systemUIPrimaryWindowController) { - mContext = context; - mResources = resources; - mUserManager = userManager; - mCarServiceProvider = carServiceProvider; - mUnlockDialogHelper = carTrustAgentUnlockDialogHelper; - mSystemUIPrimaryWindowController = systemUIPrimaryWindowController; - - mShortAnimDuration = mResources.getInteger(android.R.integer.config_shortAnimTime); - } - - /** Sets the status bar which gives an entry point to dismiss the keyguard. */ - // TODO: Remove this in favor of a keyguard controller. - public void setStatusBar(CarStatusBar statusBar) { - mCarStatusBar = statusBar; - } - - /** Returns {@code true} if the user switcher already has a parent view. */ - public boolean isAttached() { - return mParent != null; - } - - /** Sets the {@link ViewStub} to show the user switcher. */ - public void setContainer(ViewStub containerStub) { - if (isAttached()) { - return; - } - - mParent = containerStub.inflate(); - - View container = mParent.findViewById(R.id.container); - - // Initialize user grid. - mUserGridView = container.findViewById(R.id.user_grid); - GridLayoutManager layoutManager = new GridLayoutManager(mContext, - mResources.getInteger(R.integer.user_fullscreen_switcher_num_col)); - mUserGridView.setLayoutManager(layoutManager); - mUserGridView.buildAdapter(); - mUserGridView.setUserSelectionListener(this::onUserSelected); - mCarUserManagerHelper = new CarUserManagerHelper(mContext); - mCarServiceProvider.addListener( - car -> mEnrollmentManager = (CarTrustAgentEnrollmentManager) car.getCarManager( - Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE)); - - IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); - if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) { - // User0 is unlocked, switched to the initial user - showDialogForInitialUser(); - } else { - // listen to USER_UNLOCKED - mContext.registerReceiverAsUser(mUserUnlockReceiver, - UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM), - filter, - /* broadcastPermission= */ null, - /* scheduler */ null); - } - } - - private void showDialogForInitialUser() { - int initialUser = mCarUserManagerHelper.getInitialUser(); - UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser); - mSelectedUser = new UserRecord(initialUserInfo, UserRecord.FOREGROUND_USER); - - // If the initial user has screen lock and trusted device, display the unlock dialog on the - // keyguard. - if (hasScreenLock(initialUser) && hasTrustedDevice(initialUser)) { - mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser, - mOnHideListener); - } else { - // If no trusted device, dismiss the keyguard. - dismissUserSwitcher(); - } - } - - /** - * Makes user grid visible. - */ - public void show() { - if (!isAttached()) { - return; - } - mParent.setVisibility(View.VISIBLE); - mSystemUIPrimaryWindowController.setWindowExpanded(true); - } - - /** - * Hides the user grid. - */ - public void hide() { - if (!isAttached()) { - return; - } - mParent.setVisibility(View.INVISIBLE); - mSystemUIPrimaryWindowController.setWindowExpanded(false); - } - - /** - * @return {@code true} if user grid is visible, {@code false} otherwise. - */ - public boolean isVisible() { - if (!isAttached()) { - return false; - } - return mParent.getVisibility() == View.VISIBLE; - } - - /** - * Every time user clicks on an item in the switcher, if the clicked user has no trusted device, - * we hide the switcher, either gradually or immediately. - * - * If the user has trusted device, we show an unlock dialog to notify user the unlock state. - * When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher. - * - * We dismiss the entire keyguard when we hide the switcher if user clicked on the foreground - * user (user we're already logged in as). - */ - private void onUserSelected(UserGridRecyclerView.UserRecord record) { - mSelectedUser = record; - if (record.mInfo != null) { - if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) { - mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener); - return; - } - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id); - } - } - dismissUserSwitcher(); - } - - private void dismissUserSwitcher() { - if (mSelectedUser == null) { - Log.e(TAG, "Request to dismiss user switcher, but no user selected"); - return; - } - if (mSelectedUser.mType == UserRecord.FOREGROUND_USER) { - hide(); - mCarStatusBar.dismissKeyguard(); - return; - } - // Switching is about to happen, since it takes time, fade out the switcher gradually. - fadeOut(); - } - - private void fadeOut() { - mUserGridView.animate() - .alpha(0.0f) - .setDuration(mShortAnimDuration) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - hide(); - mUserGridView.setAlpha(1.0f); - } - }); - - } - - private boolean hasScreenLock(int uid) { - LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); - return lockPatternUtils.getCredentialTypeForUser(uid) - != LockPatternUtils.CREDENTIAL_TYPE_NONE; - } - - private boolean hasTrustedDevice(int uid) { - if (mEnrollmentManager == null) { // car service not ready, so it cannot be available. - return false; - } - return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty(); - } - - private OnHideListener mOnHideListener = new OnHideListener() { - @Override - public void onHide(boolean dismissUserSwitcher) { - if (dismissUserSwitcher) { - dismissUserSwitcher(); - } else { - // Re-draw the parent view, otherwise the unlock dialog will not be removed from - // the screen immediately. - mParent.invalidate(); - } - - } - }; -} diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewController.java new file mode 100644 index 000000000000..6e0db4f317ba --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewController.java @@ -0,0 +1,123 @@ +/* + * 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.window; + +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewStub; + +/** + * Owns a {@link View} that is present in SystemUIOverlayWindow. + */ +public class OverlayViewController { + private final int mStubId; + private final OverlayViewGlobalStateController mOverlayViewGlobalStateController; + + private View mLayout; + + public OverlayViewController(int stubId, + OverlayViewGlobalStateController overlayViewGlobalStateController) { + mLayout = null; + mStubId = stubId; + mOverlayViewGlobalStateController = overlayViewGlobalStateController; + } + + /** + * Shows content of {@link OverlayViewController}. + * + * Should be used to show view externally and in particular by {@link OverlayViewMediator}. + */ + public final void start() { + mOverlayViewGlobalStateController.showView(/* viewController= */ this, this::show); + } + + /** + * Hides content of {@link OverlayViewController}. + * + * Should be used to hide view externally and in particular by {@link OverlayViewMediator}. + */ + public final void stop() { + mOverlayViewGlobalStateController.hideView(/* viewController= */ this, this::hide); + } + + + /** + * Inflate layout owned by controller. + */ + public final void inflate(ViewGroup baseLayout) { + ViewStub viewStub = baseLayout.findViewById(mStubId); + mLayout = viewStub.inflate(); + onFinishInflate(); + } + + /** + * Called once inflate finishes. + */ + protected void onFinishInflate() { + // no-op + } + + /** + * Returns [@code true} if layout owned by controller has been inflated. + */ + public final boolean isInflated() { + return mLayout != null; + } + + private void show() { + if (mLayout == null) { + // layout must be inflated before show() is called. + return; + } + showInternal(); + } + + /** + * Subclasses should override this method to implement reveal animations and implement logic + * specific to when the layout owned by the controller is shown. + * + * Should only be overridden by Superclass but not called by any {@link OverlayViewMediator}. + */ + protected void showInternal() { + mLayout.setVisibility(View.VISIBLE); + } + + private void hide() { + if (mLayout == null) { + // layout must be inflated before hide() is called. + return; + } + hideInternal(); + } + + /** + * Subclasses should override this method to implement conceal animations and implement logic + * specific to when the layout owned by the controller is hidden. + * + * Should only be overridden by Superclass but not called by any {@link OverlayViewMediator}. + */ + protected void hideInternal() { + mLayout.setVisibility(View.GONE); + } + + /** + * Provides access to layout owned by controller. + */ + protected final View getLayout() { + return mLayout; + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java new file mode 100644 index 000000000000..2d4a9e6331d2 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java @@ -0,0 +1,112 @@ +/* + * 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.window; + +import android.util.Log; + +import androidx.annotation.VisibleForTesting; + +import com.android.systemui.navigationbar.car.CarNavigationBarController; + +import java.util.HashSet; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * This controller is responsible for the following: + * <p><ul> + * <li>Holds the global state for SystemUIOverlayWindow. + * <li>Allows {@link SystemUIOverlayWindowManager} to register {@link OverlayViewMediator}(s). + * <li>Enables {@link OverlayViewController)(s) to reveal/conceal themselves while respecting the + * global state of SystemUIOverlayWindow. + * </ul> + */ +@Singleton +public class OverlayViewGlobalStateController { + private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName(); + private final SystemUIOverlayWindowController mSystemUIOverlayWindowController; + private final CarNavigationBarController mCarNavigationBarController; + @VisibleForTesting + Set<String> mShownSet; + + @Inject + public OverlayViewGlobalStateController( + CarNavigationBarController carNavigationBarController, + SystemUIOverlayWindowController systemUIOverlayWindowController) { + mSystemUIOverlayWindowController = systemUIOverlayWindowController; + mSystemUIOverlayWindowController.attach(); + mCarNavigationBarController = carNavigationBarController; + mShownSet = new HashSet<>(); + } + + /** + * Register {@link OverlayViewMediator} to use in SystemUIOverlayWindow. + */ + public void registerMediator(OverlayViewMediator overlayViewMediator) { + Log.d(TAG, "Registering content mediator: " + overlayViewMediator.getClass().getName()); + + overlayViewMediator.registerListeners(); + overlayViewMediator.setupOverlayContentViewControllers(); + } + + /** + * Show content in Overlay Window. + */ + public void showView(OverlayViewController viewController, Runnable show) { + if (mShownSet.isEmpty()) { + mCarNavigationBarController.hideBars(); + mSystemUIOverlayWindowController.setWindowExpanded(true); + } + + if (!viewController.isInflated()) { + viewController.inflate(mSystemUIOverlayWindowController.getBaseLayout()); + } + + show.run(); + mShownSet.add(viewController.getClass().getName()); + + Log.d(TAG, "Content shown: " + viewController.getClass().getName()); + } + + /** + * Hide content in Overlay Window. + */ + public void hideView(OverlayViewController viewController, Runnable hide) { + if (!viewController.isInflated()) { + Log.d(TAG, "Content cannot be hidden since it isn't inflated: " + + viewController.getClass().getName()); + return; + } + if (!mShownSet.contains(viewController.getClass().getName())) { + Log.d(TAG, "Content cannot be hidden since it isn't shown: " + + viewController.getClass().getName()); + return; + } + + hide.run(); + mShownSet.remove(viewController.getClass().getName()); + + if (mShownSet.isEmpty()) { + mCarNavigationBarController.showBars(); + mSystemUIOverlayWindowController.setWindowExpanded(false); + } + + Log.d(TAG, "Content hidden: " + viewController.getClass().getName()); + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewMediator.java new file mode 100644 index 000000000000..7c34fb494de6 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewMediator.java @@ -0,0 +1,34 @@ +/* + * 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.window; + +/** + * Controls when to show and hide {@link OverlayViewController}(s). + */ +public interface OverlayViewMediator { + + /** + * Register listeners that could use ContentVisibilityAdjuster to show/hide content. + */ + void registerListeners(); + + /** + * Allows for post-inflation callbacks and listeners to be set inside required {@link + * OverlayViewController}(s). + */ + void setupOverlayContentViewControllers(); +} diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayWindowModule.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayWindowModule.java new file mode 100644 index 000000000000..b0e308966477 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayWindowModule.java @@ -0,0 +1,37 @@ +/* + * 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.window; + +import com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator; + +import dagger.Binds; +import dagger.Module; +import dagger.multibindings.ClassKey; +import dagger.multibindings.IntoMap; + +/** + * Dagger injection module for {@link SystemUIOverlayWindowManager} + */ +@Module +public abstract class OverlayWindowModule { + /** Inject into FullscreenUserSwitcherViewsMediator. */ + @Binds + @IntoMap + @ClassKey(FullscreenUserSwitcherViewMediator.class) + public abstract OverlayViewMediator bindFullscreenUserSwitcherViewsMediator( + FullscreenUserSwitcherViewMediator overlayViewsMediator); +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java b/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java index 3f55ac8ccace..9c456eecc1ac 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java +++ b/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.car; +package com.android.systemui.window; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; @@ -41,7 +41,7 @@ import javax.inject.Singleton; * this window for the notification panel. */ @Singleton -public class SystemUIPrimaryWindowController implements +public class SystemUIOverlayWindowController implements ConfigurationController.ConfigurationListener { private final Context mContext; @@ -57,7 +57,7 @@ public class SystemUIPrimaryWindowController implements private boolean mIsAttached = false; @Inject - public SystemUIPrimaryWindowController( + public SystemUIOverlayWindowController( Context context, @Main Resources resources, WindowManager windowManager, @@ -77,7 +77,7 @@ public class SystemUIPrimaryWindowController implements mLpChanged = new WindowManager.LayoutParams(); mBaseLayout = (ViewGroup) LayoutInflater.from(context) - .inflate(R.layout.sysui_primary_window, /* root= */ null, false); + .inflate(R.layout.sysui_overlay_window, /* root= */ null, false); configurationController.addCallback(this); } @@ -115,7 +115,7 @@ public class SystemUIPrimaryWindowController implements mLp.gravity = Gravity.TOP; mLp.setFitInsetsTypes(/* types= */ 0); mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; - mLp.setTitle("SystemUIPrimaryWindow"); + mLp.setTitle("SystemUIOverlayWindow"); mLp.packageName = mContext.getPackageName(); mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; diff --git a/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowManager.java b/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowManager.java new file mode 100644 index 000000000000..af0f17d50ee2 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowManager.java @@ -0,0 +1,92 @@ +/* + * 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.window; + +import android.content.Context; + +import com.android.systemui.R; +import com.android.systemui.SystemUI; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; + +/** + * Registers {@link OverlayViewMediator}(s) and synchronizes their calls to hide/show {@link + * OverlayViewController}(s) to allow for the correct visibility of system bars. + */ +@Singleton +public class SystemUIOverlayWindowManager extends SystemUI { + private static final String TAG = "SystemUIOverlayWindowManager"; + private final Map<Class<?>, Provider<OverlayViewMediator>> + mContentMediatorCreators; + private final OverlayViewGlobalStateController mOverlayViewGlobalStateController; + + @Inject + public SystemUIOverlayWindowManager( + Context context, + Map<Class<?>, Provider<OverlayViewMediator>> contentMediatorCreators, + OverlayViewGlobalStateController overlayViewGlobalStateController) { + super(context); + mContentMediatorCreators = contentMediatorCreators; + mOverlayViewGlobalStateController = overlayViewGlobalStateController; + } + + @Override + public void start() { + String[] names = mContext.getResources().getStringArray( + R.array.config_carSystemUIOverlayViewsMediators); + startServices(names); + } + + private void startServices(String[] services) { + for (String clsName : services) { + try { + OverlayViewMediator obj = resolveContentMediator(clsName); + if (obj == null) { + Constructor constructor = Class.forName(clsName).getConstructor(Context.class); + obj = (OverlayViewMediator) constructor.newInstance(this); + } + mOverlayViewGlobalStateController.registerMediator(obj); + } catch (ClassNotFoundException + | NoSuchMethodException + | IllegalAccessException + | InstantiationException + | InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + } + + private OverlayViewMediator resolveContentMediator(String className) { + return resolve(className, mContentMediatorCreators); + } + + private <T> T resolve(String className, Map<Class<?>, Provider<T>> creators) { + try { + Class<?> clazz = Class.forName(className); + Provider<T> provider = creators.get(clazz); + return provider == null ? null : provider.get(); + } catch (ClassNotFoundException e) { + return null; + } + } +} diff --git a/packages/CarSystemUI/tests/res/layout/overlay_view_controller_stub.xml b/packages/CarSystemUI/tests/res/layout/overlay_view_controller_stub.xml new file mode 100644 index 000000000000..5e5efe7614fc --- /dev/null +++ b/packages/CarSystemUI/tests/res/layout/overlay_view_controller_stub.xml @@ -0,0 +1,22 @@ +<!-- + ~ 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_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/overlay_view_controller_test"> +</LinearLayout>
\ No newline at end of file diff --git a/packages/CarSystemUI/tests/res/layout/overlay_view_controller_test.xml b/packages/CarSystemUI/tests/res/layout/overlay_view_controller_test.xml new file mode 100644 index 000000000000..165193e86c96 --- /dev/null +++ b/packages/CarSystemUI/tests/res/layout/overlay_view_controller_test.xml @@ -0,0 +1,30 @@ +<?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. + --> + +<!-- Fullscreen views in sysui should be listed here in increasing Z order. --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:background="@android:color/transparent" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ViewStub android:id="@+id/overlay_view_controller_stub" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout="@layout/overlay_view_controller_stub"/> + +</FrameLayout>
\ No newline at end of file diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java new file mode 100644 index 000000000000..3be962627f62 --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java @@ -0,0 +1,158 @@ +/* + * 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.window; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.tests.R; + +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.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class OverlayViewControllerTest extends SysuiTestCase { + private MockOverlayViewController mOverlayViewController; + private ViewGroup mBaseLayout; + + @Mock + private OverlayViewGlobalStateController mOverlayViewGlobalStateController; + + @Captor + private ArgumentCaptor<Runnable> mRunnableArgumentCaptor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(/* testClass= */ this); + + mOverlayViewController = new MockOverlayViewController(R.id.overlay_view_controller_stub, + mOverlayViewGlobalStateController); + + mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate( + R.layout.overlay_view_controller_test, /* root= */ null); + } + + @Test + public void inflate_layoutInitialized() { + mOverlayViewController.inflate(mBaseLayout); + + assertThat(mOverlayViewController.getLayout().getId()).isEqualTo( + R.id.overlay_view_controller_test); + } + + @Test + public void inflate_onFinishInflateCalled() { + mOverlayViewController.inflate(mBaseLayout); + + assertThat(mOverlayViewController.mOnFinishInflateCalled).isTrue(); + } + + @Test + public void start_viewInflated_viewShown() { + mOverlayViewController.inflate(mBaseLayout); + + mOverlayViewController.start(); + + verify(mOverlayViewGlobalStateController).showView(eq(mOverlayViewController), + mRunnableArgumentCaptor.capture()); + + mRunnableArgumentCaptor.getValue().run(); + + assertThat(mOverlayViewController.mShowInternalCalled).isTrue(); + } + + @Test + public void stop_viewInflated_viewHidden() { + mOverlayViewController.inflate(mBaseLayout); + + mOverlayViewController.stop(); + + verify(mOverlayViewGlobalStateController).hideView(eq(mOverlayViewController), + mRunnableArgumentCaptor.capture()); + + mRunnableArgumentCaptor.getValue().run(); + + assertThat(mOverlayViewController.mHideInternalCalled).isTrue(); + } + + @Test + public void start_viewNotInflated_viewNotShown() { + mOverlayViewController.start(); + + verify(mOverlayViewGlobalStateController).showView(eq(mOverlayViewController), + mRunnableArgumentCaptor.capture()); + + mRunnableArgumentCaptor.getValue().run(); + + assertThat(mOverlayViewController.mShowInternalCalled).isFalse(); + } + + @Test + public void stop_viewNotInflated_viewNotHidden() { + mOverlayViewController.stop(); + + verify(mOverlayViewGlobalStateController).hideView(eq(mOverlayViewController), + mRunnableArgumentCaptor.capture()); + + mRunnableArgumentCaptor.getValue().run(); + + assertThat(mOverlayViewController.mHideInternalCalled).isFalse(); + } + + private static class MockOverlayViewController extends OverlayViewController { + boolean mOnFinishInflateCalled = false; + boolean mShowInternalCalled = false; + boolean mHideInternalCalled = false; + + MockOverlayViewController(int stubId, + OverlayViewGlobalStateController overlayViewGlobalStateController) { + super(stubId, overlayViewGlobalStateController); + } + + @Override + protected void onFinishInflate() { + mOnFinishInflateCalled = true; + } + + @Override + protected void showInternal() { + mShowInternalCalled = true; + } + + @Override + protected void hideInternal() { + mHideInternalCalled = true; + } + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewGlobalStateControllerTest.java new file mode 100644 index 000000000000..03354d1aa9d7 --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewGlobalStateControllerTest.java @@ -0,0 +1,250 @@ +/* + * 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.window; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.navigationbar.car.CarNavigationBarController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { + private static final String MOCK_OVERLAY_VIEW_CONTROLLER_NAME = "OverlayViewController"; + + private OverlayViewGlobalStateController mOverlayViewGlobalStateController; + private ViewGroup mBaseLayout; + + @Mock + private CarNavigationBarController mCarNavigationBarController; + @Mock + private SystemUIOverlayWindowController mSystemUIOverlayWindowController; + @Mock + private OverlayViewMediator mOverlayViewMediator; + @Mock + private OverlayViewController mOverlayViewController; + @Mock + private Runnable mRunnable; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(/* testClass= */ this); + + mOverlayViewGlobalStateController = new OverlayViewGlobalStateController( + mCarNavigationBarController, mSystemUIOverlayWindowController); + + verify(mSystemUIOverlayWindowController).attach(); + + mBaseLayout = new FrameLayout(mContext); + + when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout); + } + + @Test + public void registerMediator_overlayViewMediatorListenersRegistered() { + mOverlayViewGlobalStateController.registerMediator(mOverlayViewMediator); + + verify(mOverlayViewMediator).registerListeners(); + } + + @Test + public void registerMediator_overlayViewMediatorViewControllerSetup() { + mOverlayViewGlobalStateController.registerMediator(mOverlayViewMediator); + + verify(mOverlayViewMediator).setupOverlayContentViewControllers(); + } + + @Test + public void showView_nothingAlreadyShown_navigationBarsHidden() { + mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable); + + verify(mCarNavigationBarController).hideBars(); + } + + @Test + public void showView_nothingAlreadyShown_windowIsExpanded() { + mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable); + + verify(mSystemUIOverlayWindowController).setWindowExpanded(true); + } + + @Test + public void showView_somethingAlreadyShown_navigationBarsHidden() { + mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME); + + mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable); + + verify(mCarNavigationBarController, never()).hideBars(); + } + + @Test + public void showView_somethingAlreadyShown_windowIsExpanded() { + mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME); + + mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable); + + verify(mSystemUIOverlayWindowController, never()).setWindowExpanded(true); + } + + @Test + public void showView_viewControllerNotInflated_inflateViewController() { + when(mOverlayViewController.isInflated()).thenReturn(false); + + mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable); + + verify(mOverlayViewController).inflate(mBaseLayout); + } + + @Test + public void showView_viewControllerInflated_inflateViewControllerNotCalled() { + when(mOverlayViewController.isInflated()).thenReturn(true); + + mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable); + + verify(mOverlayViewController, never()).inflate(mBaseLayout); + } + + @Test + public void showView_showRunnableCalled() { + mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable); + + verify(mRunnable).run(); + } + + @Test + public void showView_overlayViewControllerAddedToShownSet() { + mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable); + + assertThat(mOverlayViewGlobalStateController.mShownSet.contains( + mOverlayViewController.getClass().getName())).isTrue(); + } + + @Test + public void hideView_viewControllerNotInflated_hideRunnableNotCalled() { + when(mOverlayViewController.isInflated()).thenReturn(false); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable); + + verify(mRunnable, never()).run(); + } + + @Test + public void hideView_nothingShown_hideRunnableNotCalled() { + when(mOverlayViewController.isInflated()).thenReturn(true); + mOverlayViewGlobalStateController.mShownSet.clear(); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable); + + verify(mRunnable, never()).run(); + } + + @Test + public void hideView_viewControllerNotShown_hideRunnableNotCalled() { + when(mOverlayViewController.isInflated()).thenReturn(true); + mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable); + + verify(mRunnable, never()).run(); + } + + @Test + public void hideView_viewControllerShown_hideRunnableCalled() { + when(mOverlayViewController.isInflated()).thenReturn(true); + mOverlayViewGlobalStateController.mShownSet.add( + mOverlayViewController.getClass().getName()); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable); + + verify(mRunnable).run(); + } + + @Test + public void hideView_viewControllerOnlyShown_nothingShown() { + when(mOverlayViewController.isInflated()).thenReturn(true); + mOverlayViewGlobalStateController.mShownSet.add( + mOverlayViewController.getClass().getName()); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable); + + assertThat(mOverlayViewGlobalStateController.mShownSet.isEmpty()).isTrue(); + } + + @Test + public void hideView_viewControllerNotOnlyShown_navigationBarNotShown() { + when(mOverlayViewController.isInflated()).thenReturn(true); + mOverlayViewGlobalStateController.mShownSet.add( + mOverlayViewController.getClass().getName()); + mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable); + + verify(mCarNavigationBarController, never()).showBars(); + } + + @Test + public void hideView_viewControllerNotOnlyShown_windowNotCollapsed() { + when(mOverlayViewController.isInflated()).thenReturn(true); + mOverlayViewGlobalStateController.mShownSet.add( + mOverlayViewController.getClass().getName()); + mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable); + + verify(mSystemUIOverlayWindowController, never()).setWindowExpanded(false); + } + + @Test + public void hideView_viewControllerOnlyShown_navigationBarShown() { + when(mOverlayViewController.isInflated()).thenReturn(true); + mOverlayViewGlobalStateController.mShownSet.add( + mOverlayViewController.getClass().getName()); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable); + + verify(mCarNavigationBarController).showBars(); + } + + @Test + public void hideView_viewControllerOnlyShown_windowCollapsed() { + when(mOverlayViewController.isInflated()).thenReturn(true); + mOverlayViewGlobalStateController.mShownSet.add( + mOverlayViewController.getClass().getName()); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable); + + verify(mSystemUIOverlayWindowController).setWindowExpanded(false); + } +} diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java index 26bcd54930b6..bd9dc4ff1e3c 100644 --- a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java +++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java @@ -16,6 +16,8 @@ package com.android.settingslib.schedulesprovider; +import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; @@ -25,25 +27,25 @@ import androidx.annotation.NonNull; /** * Schedule data item containing the schedule title text, the summary text which is displayed on the - * summary of the Settings preference and an {@link Intent} which Settings will launch when the - * user clicks on the preference. + * summary of the Settings preference and a {@link PendingIntent} which Settings will launch + * when the user clicks on the preference. */ public class ScheduleInfo implements Parcelable { private static final String TAG = "ScheduleInfo"; private final String mTitle; private final String mSummary; - private final Intent mIntent; + private final PendingIntent mPendingIntent; public ScheduleInfo(Builder builder) { mTitle = builder.mTitle; mSummary = builder.mSummary; - mIntent = builder.mIntent; + mPendingIntent = builder.mPendingIntent; } private ScheduleInfo(Parcel in) { mTitle = in.readString(); mSummary = in.readString(); - mIntent = in.readParcelable(Intent.class.getClassLoader()); + mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader()); } /** @@ -61,11 +63,11 @@ public class ScheduleInfo implements Parcelable { } /** - * Returns an {@link Intent} which Settings will launch when the user clicks on a schedule - * preference. + * Returns a {@link PendingIntent} which Settings will launch when the user clicks on a + * schedule preference. */ - public Intent getIntent() { - return mIntent; + public PendingIntent getPendingIntent() { + return mPendingIntent; } /** @@ -74,14 +76,15 @@ public class ScheduleInfo implements Parcelable { * @return {@code true} if all member variables are valid. */ public boolean isValid() { - return !TextUtils.isEmpty(mTitle) && !TextUtils.isEmpty(mSummary) && (mIntent != null); + return !TextUtils.isEmpty(mTitle) && !TextUtils.isEmpty(mSummary) + && (mPendingIntent != null); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mTitle); dest.writeString(mSummary); - dest.writeParcelable(mIntent, flags); + dest.writeParcelable(mPendingIntent, flags); } @Override @@ -104,7 +107,7 @@ public class ScheduleInfo implements Parcelable { @NonNull @Override public String toString() { - return "title: " + mTitle + ", summary: " + mSummary + ", intent: " + mIntent; + return "title: " + mTitle + ", summary: " + mSummary + ", pendingIntent: " + mPendingIntent; } /** @@ -113,7 +116,7 @@ public class ScheduleInfo implements Parcelable { public static class Builder { private String mTitle; private String mSummary; - private Intent mIntent; + private PendingIntent mPendingIntent; /** * Sets the title. @@ -138,13 +141,15 @@ public class ScheduleInfo implements Parcelable { } /** - * Sets the {@link Intent}. + * Sets the {@link PendingIntent}. + * <p>The {@link PendingIntent} should be created with + * {@link PendingIntent#getActivity(Context, int, Intent, int)}. * - * @param intent The action when user clicks the preference. + * @param pendingIntent The pending intent to send when the user clicks the preference. * @return This instance. */ - public Builder setIntent(@NonNull Intent intent) { - mIntent = intent; + public Builder setPendingIntent(@NonNull PendingIntent pendingIntent) { + mPendingIntent = pendingIntent; return this; } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 3ae9e1ed78c3..7e78a78852a8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -311,6 +311,21 @@ public class InfoMediaManager extends MediaManager { return -1; } + CharSequence getSessionName() { + if (TextUtils.isEmpty(mPackageName)) { + Log.w(TAG, "Unable to get session name. The package name is null or empty!"); + return null; + } + + final RoutingSessionInfo info = getRoutingSessionInfo(); + if (info != null) { + return info.getName(); + } + + Log.w(TAG, "Unable to get session name for package: " + mPackageName); + return null; + } + private void refreshDevices() { mMediaDevices.clear(); mCurrentConnectedDevice = null; diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index c70811f1f20e..adb3c1170f8f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -324,6 +324,15 @@ public class LocalMediaManager implements BluetoothCallback { return mInfoMediaManager.getSessionVolume(); } + /** + * Gets the user-visible name of the {@link android.media.RoutingSessionInfo}. + * + * @return current name of the session, and return {@code null} if not found. + */ + public CharSequence getSessionName() { + return mInfoMediaManager.getSessionName(); + } + private MediaDevice updateCurrentConnectedDevice() { MediaDevice phoneMediaDevice = null; for (MediaDevice device : mMediaDevices) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index edb121b762a7..7cd0a7ce0437 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -460,4 +460,37 @@ public class InfoMediaManagerTest { assertThat(mInfoMediaManager.releaseSession()).isTrue(); } + + @Test + public void getSessionName_packageNameIsNull_returnNull() { + mInfoMediaManager.mPackageName = null; + + assertThat(mInfoMediaManager.getSessionName()).isNull(); + } + + @Test + public void getSessionName_notContainPackageName_returnNull() { + final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>(); + final RoutingSessionInfo info = mock(RoutingSessionInfo.class); + routingSessionInfos.add(info); + + mShadowRouter2Manager.setRoutingSessions(routingSessionInfos); + when(info.getClientPackageName()).thenReturn("com.fake.packagename"); + when(info.getName()).thenReturn(TEST_NAME); + + assertThat(mInfoMediaManager.getSessionName()).isNull(); + } + + @Test + public void getSessionName_containPackageName_returnName() { + final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>(); + final RoutingSessionInfo info = mock(RoutingSessionInfo.class); + routingSessionInfos.add(info); + + mShadowRouter2Manager.setRoutingSessions(routingSessionInfos); + when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(info.getName()).thenReturn(TEST_NAME); + + assertThat(mInfoMediaManager.getSessionName()).isEqualTo(TEST_NAME); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java index 5ec89eddda6e..b4b910d21da8 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java @@ -17,11 +17,14 @@ package com.android.settingslib.schedulesprovider; import static com.google.common.truth.Truth.assertThat; +import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class ScheduleInfoTest { @@ -29,10 +32,12 @@ public class ScheduleInfoTest { private static final String TEST_SUMMARY = "Night Light summary"; private static final String TEST_EMPTY_SUMMARY = ""; + private final Context mContext = RuntimeEnvironment.application; + @Test public void builder_usedValidArguments_isValid() { - final Intent intent = createTestIntent(); - final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent); + final PendingIntent pendingIntent = createTestPendingIntent(mContext); + final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, pendingIntent); assertThat(info).isNotNull(); assertThat(info.isValid()).isTrue(); @@ -40,15 +45,16 @@ public class ScheduleInfoTest { @Test public void builder_useEmptySummary_isInvalid() { - final Intent intent = createTestIntent(); - final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_EMPTY_SUMMARY, intent); + final PendingIntent pendingIntent = createTestPendingIntent(mContext); + final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_EMPTY_SUMMARY, + pendingIntent); assertThat(info).isNotNull(); assertThat(info.isValid()).isFalse(); } @Test - public void builder_intentIsNull_isInvalid() { + public void builder_pendingIntentIsNull_isInvalid() { final ScheduleInfo info = new ScheduleInfo.Builder() .setTitle(TEST_TITLE) .setSummary(TEST_SUMMARY) @@ -60,39 +66,40 @@ public class ScheduleInfoTest { @Test public void getTitle_setValidTitle_shouldReturnSameCorrectTitle() { - final Intent intent = createTestIntent(); - final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent); + final PendingIntent pendingIntent = createTestPendingIntent(mContext); + final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, pendingIntent); assertThat(info.getTitle()).isEqualTo(TEST_TITLE); } @Test public void getSummary_setValidSummary_shouldReturnSameCorrectSummary() { - final Intent intent = createTestIntent(); - final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent); + final PendingIntent pendingIntent = createTestPendingIntent(mContext); + final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, pendingIntent); assertThat(info.getSummary()).isEqualTo(TEST_SUMMARY); } @Test - public void getIntent_setValidIntent_shouldReturnSameCorrectIntent() { - final Intent intent = createTestIntent(); - final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent); + public void getPendingIntent_setValidPendingIntent_shouldReturnSameCorrectIntent() { + final PendingIntent pendingIntent = createTestPendingIntent(mContext); + final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, pendingIntent); - assertThat(info.getIntent()).isEqualTo(intent); + assertThat(info.getPendingIntent()).isEqualTo(pendingIntent); } - private static Intent createTestIntent() { - return new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory( + private static PendingIntent createTestPendingIntent(Context context) { + final Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory( Intent.CATEGORY_DEFAULT); + return PendingIntent.getActivity(context, 0 /* requestCode */, intent, 0 /* flags */); } private static ScheduleInfo createTestScheduleInfo(String title, String summary, - Intent intent) { + PendingIntent pendingIntent) { return new ScheduleInfo.Builder() .setTitle(title) .setSummary(summary) - .setIntent(intent) + .setPendingIntent(pendingIntent) .build(); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java index eb2e8e055378..6b92082b1103 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java @@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.robolectric.Shadows.shadowOf; +import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -27,6 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; @@ -35,13 +38,16 @@ public class SchedulesProviderTest { private static final String INVALID_PACKAGE = "com.android.sunny"; private static final String VALID_PACKAGE = "com.android.settings"; private static final String INVALID_METHOD = "queryTestData"; + + private final Context mContext = RuntimeEnvironment.application; + private TestSchedulesProvider mProvider; @Before public void setUp() { mProvider = Robolectric.setupContentProvider(TestSchedulesProvider.class); shadowOf(mProvider).setCallingPackage(VALID_PACKAGE); - mProvider.setScheduleInfos(TestSchedulesProvider.createOneValidScheduleInfo()); + mProvider.setScheduleInfos(TestSchedulesProvider.createOneValidScheduleInfo(mContext)); } @Test @@ -76,7 +82,7 @@ public class SchedulesProviderTest { @Test public void call_addTwoValidData_returnScheduleInfoData() { - mProvider.setScheduleInfos(TestSchedulesProvider.createTwoValidScheduleInfos()); + mProvider.setScheduleInfos(TestSchedulesProvider.createTwoValidScheduleInfos(mContext)); final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST, null /* arg */, null /* extras */); @@ -89,7 +95,8 @@ public class SchedulesProviderTest { @Test public void call_addTwoValidDataAndOneInvalidData_returnTwoScheduleInfoData() { - mProvider.setScheduleInfos(TestSchedulesProvider.createTwoValidAndOneInvalidScheduleInfo()); + mProvider.setScheduleInfos( + TestSchedulesProvider.createTwoValidAndOneInvalidScheduleInfo(mContext)); final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST, null /* arg */, null /* extras */); @@ -112,55 +119,56 @@ public class SchedulesProviderTest { mScheduleInfos = scheduleInfos; } - private static ArrayList<ScheduleInfo> createOneValidScheduleInfo() { + private static ArrayList<ScheduleInfo> createOneValidScheduleInfo(Context context) { final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>(); - final Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory( - Intent.CATEGORY_DEFAULT); - final ScheduleInfo info = new ScheduleInfo.Builder().setTitle( - "Night Light").setSummary("This a sunny test").setIntent(intent).build(); + + final ScheduleInfo info = new ScheduleInfo.Builder().setTitle("Night Light").setSummary( + "This a sunny test").setPendingIntent(createTestPendingIntent(context, + "android.settings.NIGHT_DISPLAY_SETTINGS")).build(); scheduleInfos.add(info); return scheduleInfos; } - private static ArrayList<ScheduleInfo> createTwoValidScheduleInfos() { + private static ArrayList<ScheduleInfo> createTwoValidScheduleInfos(Context context) { final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>(); - Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory( - Intent.CATEGORY_DEFAULT); - ScheduleInfo info = new ScheduleInfo.Builder().setTitle( - "Night Light").setSummary("This a sunny test").setIntent(intent).build(); + ScheduleInfo info = new ScheduleInfo.Builder().setTitle("Night Light").setSummary( + "This a sunny test").setPendingIntent(createTestPendingIntent(context, + "android.settings.NIGHT_DISPLAY_SETTINGS")).build(); scheduleInfos.add(info); - intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory( - Intent.CATEGORY_DEFAULT); info = new ScheduleInfo.Builder().setTitle("Display").setSummary( - "Display summary").setIntent(intent).build(); + "Display summary").setPendingIntent( + createTestPendingIntent(context, "android.settings.DISPLAY_SETTINGS")).build(); scheduleInfos.add(info); return scheduleInfos; } - private static ArrayList<ScheduleInfo> createTwoValidAndOneInvalidScheduleInfo() { + private static ArrayList<ScheduleInfo> createTwoValidAndOneInvalidScheduleInfo( + Context context) { final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>(); - Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory( - Intent.CATEGORY_DEFAULT); - ScheduleInfo info = new ScheduleInfo.Builder().setTitle( - "Night Light").setSummary("This a sunny test").setIntent(intent).build(); + ScheduleInfo info = new ScheduleInfo.Builder().setTitle("Night Light").setSummary( + "This a sunny test").setPendingIntent(createTestPendingIntent(context, + "android.settings.NIGHT_DISPLAY_SETTINGS")).build(); scheduleInfos.add(info); - intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory( - Intent.CATEGORY_DEFAULT); info = new ScheduleInfo.Builder().setTitle("Display").setSummary( - "Display summary").setIntent(intent).build(); + "Display summary").setPendingIntent( + createTestPendingIntent(context, "android.settings.DISPLAY_SETTINGS")).build(); scheduleInfos.add(info); - intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory( - Intent.CATEGORY_DEFAULT); - info = new ScheduleInfo.Builder().setTitle("").setSummary("Display summary").setIntent( - intent).build(); + info = new ScheduleInfo.Builder().setTitle("").setSummary( + "Display summary").setPendingIntent( + createTestPendingIntent(context, "android.settings.DISPLAY_SETTINGS")).build(); scheduleInfos.add(info); return scheduleInfos; } + + private static PendingIntent createTestPendingIntent(Context context, String action) { + final Intent intent = new Intent(action).addCategory(Intent.CATEGORY_DEFAULT); + return PendingIntent.getActivity(context, 0 /* requestCode */, intent, 0 /* flags */); + } } } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 2431381f3033..8fa98c85fc33 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -162,5 +162,6 @@ public class SecureSettings { Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, Settings.Secure.PEOPLE_STRIP, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, + Settings.Secure.ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 5553469b0420..75c5f9581820 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -245,5 +245,8 @@ public class SecureSettingsValidators { new InclusiveIntegerRangeValidator( Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)); + VALIDATORS.put( + Secure.ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS, + ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index d6776879254d..8789a5c62c6c 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1811,6 +1811,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, SecureSettingsProto.Accessibility.ACCESSIBILITY_MAGNIFICATION_MODE); + dumpSetting(s, p, + Settings.Secure.ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS, + SecureSettingsProto.Accessibility.BUTTON_LONG_PRESS_TARGETS); p.end(accessibilityToken); final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP); diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml index 84606126086d..6a7f9e2620db 100644 --- a/packages/SystemUI/res/layout/notification_conversation_info.xml +++ b/packages/SystemUI/res/layout/notification_conversation_info.xml @@ -121,7 +121,6 @@ android:layout_marginEnd="2dp" android:ellipsize="end" android:text="@string/notification_delegate_header" - android:layout_toEndOf="@id/pkg_divider" android:maxLines="1" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 5d03eee11f75..6ab573bbfba7 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -27,51 +27,81 @@ android:paddingStart="@*android:dimen/notification_content_margin_start"> <!-- Package Info --> - <RelativeLayout + <LinearLayout android:id="@+id/header" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="@dimen/notification_guts_conversation_header_height" + android:gravity="center_vertical" android:clipChildren="false" android:clipToPadding="false"> <ImageView - android:id="@+id/pkgicon" - android:layout_width="@dimen/notification_guts_header_height" - android:layout_height="@dimen/notification_guts_header_height" + android:id="@+id/pkg_icon" + android:layout_width="@dimen/notification_guts_conversation_icon_size" + android:layout_height="@dimen/notification_guts_conversation_icon_size" android:layout_centerVertical="true" android:layout_alignParentStart="true" - android:layout_marginEnd="3dp" /> - <TextView - android:id="@+id/pkgname" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - style="@style/TextAppearance.NotificationImportanceHeader" - android:layout_marginStart="3dp" - android:layout_marginEnd="2dp" - android:layout_toEndOf="@id/pkgicon" - android:singleLine="true" /> - <TextView - android:id="@+id/pkg_divider" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - style="@style/TextAppearance.NotificationImportanceHeader" - android:layout_marginStart="2dp" - android:layout_marginEnd="2dp" - android:layout_toEndOf="@id/pkgname" - android:text="@*android:string/notification_header_divider_symbol" /> - <TextView - android:id="@+id/delegate_name" - android:layout_width="wrap_content" + android:layout_marginEnd="15dp" /> + <LinearLayout + android:id="@+id/names" + android:layout_weight="1" + android:layout_width="0dp" + android:orientation="vertical" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_guts_conversation_icon_size" android:layout_centerVertical="true" - style="@style/TextAppearance.NotificationImportanceHeader" - android:layout_marginStart="2dp" - android:layout_marginEnd="2dp" - android:ellipsize="end" - android:text="@string/notification_delegate_header" - android:layout_toEndOf="@id/pkg_divider" - android:maxLines="1" /> + android:gravity="center_vertical" + android:layout_alignEnd="@id/pkg_icon" + android:layout_toEndOf="@id/pkg_icon" + android:layout_alignStart="@id/mute"> + <TextView + android:id="@+id/channel_name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/TextAppearance.NotificationImportanceChannel"/> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="start" + android:orientation="horizontal"> + <TextView + android:id="@+id/pkg_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@style/TextAppearance.NotificationImportanceChannelGroup" + android:ellipsize="end" + android:maxLines="1"/> + <TextView + android:id="@+id/group_divider" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + style="@style/TextAppearance.NotificationImportanceHeader" + android:layout_marginStart="2dp" + android:layout_marginEnd="2dp" + android:text="@*android:string/notification_header_divider_symbol" /> + <TextView + android:id="@+id/group_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + style="@style/TextAppearance.NotificationImportanceChannel"/> + </LinearLayout> + <TextView + android:id="@+id/delegate_name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + style="@style/TextAppearance.NotificationImportanceHeader" + android:layout_marginStart="2dp" + android:layout_marginEnd="2dp" + android:ellipsize="end" + android:text="@string/notification_delegate_header" + android:maxLines="1" /> + + </LinearLayout> + + <!-- end aligned fields --> <!-- Optional link to app. Only appears if the channel is not disabled and the app asked for it --> <ImageButton @@ -95,91 +125,6 @@ asked for it --> android:src="@drawable/ic_settings" android:layout_alignParentEnd="true" android:tint="@color/notification_guts_link_icon_tint"/> - </RelativeLayout> - - <!-- Channel Info Block --> - <LinearLayout - android:id="@+id/channel_info" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingEnd="@*android:dimen/notification_content_margin_end" - android:gravity="center" - android:orientation="vertical"> - <!-- Channel Name --> - <TextView - android:id="@+id/channel_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" - style="@style/TextAppearance.NotificationImportanceChannel"/> - <TextView - android:id="@+id/group_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@style/TextAppearance.NotificationImportanceChannelGroup" - android:ellipsize="end" - android:maxLines="1"/> - </LinearLayout> - - <LinearLayout - android:id="@+id/blocking_helper" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/notification_guts_button_spacing" - android:layout_marginBottom="@dimen/notification_guts_button_spacing" - android:paddingEnd="@*android:dimen/notification_content_margin_end" - android:clipChildren="false" - android:clipToPadding="false" - android:orientation="vertical"> - <!-- blocking helper text. no need for non-configurable check b/c controls won't be - activated in that case --> - <TextView - android:id="@+id/blocking_helper_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="2dp" - android:text="@string/inline_blocking_helper" - style="@*android:style/TextAppearance.DeviceDefault.Notification" /> - <RelativeLayout - android:id="@+id/block_buttons" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/notification_guts_button_spacing"> - <TextView - android:id="@+id/blocking_helper_turn_off_notifications" - android:text="@string/inline_turn_off_notifications" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_alignParentStart="true" - android:width="110dp" - android:paddingEnd="15dp" - android:breakStrategy="simple" - style="@style/TextAppearance.NotificationInfo.Button"/> - <TextView - android:id="@+id/deliver_silently" - android:text="@string/inline_deliver_silently_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" - android:paddingEnd="15dp" - android:width="110dp" - android:breakStrategy="simple" - android:layout_toStartOf="@+id/keep_showing" - style="@style/TextAppearance.NotificationInfo.Button"/> - <TextView - android:id="@+id/keep_showing" - android:text="@string/inline_keep_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" - android:width="110dp" - android:breakStrategy="simple" - android:layout_alignParentEnd="true" - style="@style/TextAppearance.NotificationInfo.Button"/> - </RelativeLayout> </LinearLayout> @@ -357,34 +302,4 @@ asked for it --> </RelativeLayout> </LinearLayout> - - <com.android.systemui.statusbar.notification.row.NotificationUndoLayout - android:id="@+id/confirmation" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:visibility="gone" - android:orientation="horizontal" > - <TextView - android:id="@+id/confirmation_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="start|center_vertical" - android:layout_marginStart="@*android:dimen/notification_content_margin_start" - android:layout_marginEnd="@*android:dimen/notification_content_margin_start" - android:text="@string/notification_channel_disabled" - style="@style/TextAppearance.NotificationInfo.Confirmation"/> - <TextView - android:id="@+id/undo" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:minWidth="@dimen/notification_importance_toggle_size" - android:minHeight="@dimen/notification_importance_toggle_size" - android:layout_marginTop="@dimen/notification_guts_button_spacing" - android:layout_marginBottom="@dimen/notification_guts_button_spacing" - android:layout_marginStart="@dimen/notification_guts_button_side_margin" - android:layout_marginEnd="@dimen/notification_guts_button_side_margin" - android:layout_gravity="end|center_vertical" - android:text="@string/inline_undo" - style="@style/TextAppearance.NotificationInfo.Button"/> - </com.android.systemui.statusbar.notification.row.NotificationUndoLayout> </com.android.systemui.statusbar.notification.row.NotificationInfo> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java index e554dcd36100..ebed1fcdb48b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java @@ -64,7 +64,7 @@ public class InputConsumerController { private final class InputEventReceiver extends BatchedInputEventReceiver { public InputEventReceiver(InputChannel inputChannel, Looper looper) { - super(inputChannel, looper, Choreographer.getSfInstance()); + super(inputChannel, looper, Choreographer.getInstance()); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 6a6255f616a3..4508fc74e884 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -23,7 +23,6 @@ import static android.content.Intent.ACTION_USER_STOPPED; import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; -import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; @@ -455,7 +454,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ public List<SubscriptionInfo> getFilteredSubscriptionInfo(boolean forceReload) { List<SubscriptionInfo> subscriptions = getSubscriptionInfo(false); - if (subscriptions.size() == MODEM_COUNT_DUAL_MODEM) { + if (subscriptions.size() == 2) { SubscriptionInfo info1 = subscriptions.get(0); SubscriptionInfo info2 = subscriptions.get(1); if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) { diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 1324524c4011..86aa6409ab27 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -28,6 +28,9 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.annotation.Dimension; import android.annotation.NonNull; import android.app.ActivityManager; @@ -714,6 +717,8 @@ public class ScreenDecorations extends SystemUI implements Tunable { public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener, RegionInterceptableView { + private static final float HIDDEN_CAMERA_PROTECTION_SCALE = 0.5f; + private final DisplayInfo mInfo = new DisplayInfo(); private final Paint mPaint = new Paint(); private final List<Rect> mBounds = new ArrayList(); @@ -732,6 +737,8 @@ public class ScreenDecorations extends SystemUI implements Tunable { private int mRotation; private int mInitialPosition; private int mPosition; + private float mCameraProtectionProgress = HIDDEN_CAMERA_PROTECTION_SCALE; + private ValueAnimator mCameraProtectionAnimator; public DisplayCutoutView(Context context, @BoundsPosition int pos, ScreenDecorations decorations) { @@ -770,17 +777,18 @@ public class ScreenDecorations extends SystemUI implements Tunable { getLocationOnScreen(mLocation); canvas.translate(-mLocation[0], -mLocation[1]); - if (mShowProtection && !mProtectionRect.isEmpty()) { - mPaint.setColor(mColor); - mPaint.setStyle(Paint.Style.FILL); - mPaint.setAntiAlias(true); - canvas.drawPath(mProtectionPath, mPaint); - } else if (!mBoundingPath.isEmpty()) { + if (!mBoundingPath.isEmpty()) { mPaint.setColor(mColor); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); canvas.drawPath(mBoundingPath, mPaint); } + if (mCameraProtectionProgress > HIDDEN_CAMERA_PROTECTION_SCALE + && !mProtectionRect.isEmpty()) { + canvas.scale(mCameraProtectionProgress, mCameraProtectionProgress, + mProtectionRect.centerX(), mProtectionRect.centerY()); + canvas.drawPath(mProtectionPath, mPaint); + } } @Override @@ -815,8 +823,31 @@ public class ScreenDecorations extends SystemUI implements Tunable { mShowProtection = shouldShow; updateBoundingPath(); - requestLayout(); - invalidate(); + // Delay the relayout until the end of the animation when hiding the cutout, + // otherwise we'd clip it. + if (mShowProtection) { + requestLayout(); + } + if (mCameraProtectionAnimator != null) { + mCameraProtectionAnimator.cancel(); + } + mCameraProtectionAnimator = ValueAnimator.ofFloat(mCameraProtectionProgress, + mShowProtection ? 1.0f : HIDDEN_CAMERA_PROTECTION_SCALE).setDuration(750); + mCameraProtectionAnimator.setInterpolator(Interpolators.DECELERATE_QUINT); + mCameraProtectionAnimator.addUpdateListener(animation -> { + mCameraProtectionProgress = (float) animation.getAnimatedValue(); + invalidate(); + }); + mCameraProtectionAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mCameraProtectionAnimator = null; + if (!mShowProtection) { + requestLayout(); + } + } + }); + mCameraProtectionAnimator.start(); } private void update() { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java index 2873811aeffb..b33eeba5da70 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java @@ -73,6 +73,9 @@ public class BubbleExperimentConfig { private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps"; + private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow"; + private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false; + /** * When true, if a notification has the information necessary to bubble (i.e. valid * contentIntent and an icon or image), then a {@link android.app.Notification.BubbleMetadata} @@ -87,6 +90,15 @@ public class BubbleExperimentConfig { } /** + * When true, show a menu with dismissed and aged-out bubbles. + */ + static boolean allowBubbleOverflow(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ALLOW_BUBBLE_OVERFLOW, + ALLOW_BUBBLE_OVERFLOW_DEFAULT ? 1 : 0) != 0; + } + + /** * Same as {@link #allowAnyNotifToBubble(Context)} except it filters for notifications that * are using {@link Notification.MessagingStyle} and have remote input. */ diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 6647069f2033..0cf6d89e8d74 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -589,6 +589,9 @@ public class BubbleStackView extends FrameLayout { } private void setUpOverflow() { + if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) { + return; + } int overflowBtnIndex = 0; if (mBubbleOverflow == null) { mBubbleOverflow = new BubbleOverflow(getContext()); @@ -800,7 +803,8 @@ public class BubbleStackView extends FrameLayout { @Nullable Bubble getExpandedBubble() { if (mExpandedBubble == null - || (mExpandedBubble.getIconView() == mBubbleOverflow.getBtn() + || (BubbleExperimentConfig.allowBubbleOverflow(mContext) + && mExpandedBubble.getIconView() == mBubbleOverflow.getBtn() && BubbleOverflow.KEY.equals(mExpandedBubble.getKey()))) { return null; } @@ -857,6 +861,9 @@ public class BubbleStackView extends FrameLayout { } private void updateOverflowBtnVisibility(boolean apply) { + if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) { + return; + } if (mIsExpanded) { if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "Show overflow button."); @@ -1083,7 +1090,8 @@ public class BubbleStackView extends FrameLayout { float y = event.getRawY(); if (mIsExpanded) { if (isIntersecting(mBubbleContainer, x, y)) { - if (isIntersecting(mBubbleOverflow.getBtn(), x, y)) { + if (BubbleExperimentConfig.allowBubbleOverflow(mContext) + && isIntersecting(mBubbleOverflow.getBtn(), x, y)) { return mBubbleOverflow.getBtn(); } // Could be tapping or dragging a bubble while expanded @@ -1820,8 +1828,11 @@ public class BubbleStackView extends FrameLayout { * @return the number of bubbles in the stack view. */ public int getBubbleCount() { - // Subtract 1 for the overflow button that is always in the bubble container. - return mBubbleContainer.getChildCount() - 1; + if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) { + // Subtract 1 for the overflow button that is always in the bubble container. + return mBubbleContainer.getChildCount() - 1; + } + return mBubbleContainer.getChildCount(); } /** 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 f2c84906c868..6eed6b86f8db 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -28,6 +28,7 @@ import android.service.controls.templates.TemperatureControlTemplate import android.service.controls.templates.ThumbnailTemplate import android.service.controls.templates.ToggleRangeTemplate import android.service.controls.templates.ToggleTemplate +import android.util.Log import android.view.View import android.view.ViewGroup import android.widget.ImageView @@ -97,15 +98,8 @@ class ControlViewHolder( } fun actionResponse(@ControlAction.ResponseResult response: Int) { - val text = when (response) { - ControlAction.RESPONSE_OK -> "Success" - ControlAction.RESPONSE_FAIL -> "Error" - else -> "" - } - - if (!text.isEmpty()) { - setTransientStatus(text) - } + // TODO: b/150931809 - handle response codes + Log.d(ControlsUiController.TAG, "Received response code: $response") } fun setTransientStatus(tempStatus: String) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java index 1fc1fe45bbb3..36b5fade1929 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -19,11 +19,8 @@ package com.android.systemui.pip; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.IntDef; -import android.annotation.MainThread; import android.content.Context; import android.graphics.Rect; -import android.os.RemoteException; -import android.view.IWindowContainer; import android.view.SurfaceControl; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -61,31 +58,30 @@ public class PipAnimationController { com.android.internal.R.interpolator.fast_out_slow_in); } - @MainThread - PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip, + PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip, Rect destinationBounds, float alphaStart, float alphaEnd) { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( - PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip, - destinationBounds, alphaStart, alphaEnd)); + PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds, + alphaStart, alphaEnd)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA && mCurrentAnimator.isRunning()) { mCurrentAnimator.updateEndValue(alphaEnd); } else { mCurrentAnimator.cancel(); mCurrentAnimator = setupPipTransitionAnimator( - PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip, - destinationBounds, alphaStart, alphaEnd)); + PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds, + alphaStart, alphaEnd)); } return mCurrentAnimator; } - @MainThread - PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip, + PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip, Rect startBounds, Rect endBounds) { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( - PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds)); + PipTransitionAnimator.ofBounds(leash, scheduleFinishPip, + startBounds, endBounds)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS && mCurrentAnimator.isRunning()) { mCurrentAnimator.setDestinationBounds(endBounds); @@ -94,7 +90,8 @@ public class PipAnimationController { } else { mCurrentAnimator.cancel(); mCurrentAnimator = setupPipTransitionAnimator( - PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds)); + PipTransitionAnimator.ofBounds(leash, scheduleFinishPip, + startBounds, endBounds)); } return mCurrentAnimator; } @@ -116,18 +113,18 @@ public class PipAnimationController { /** * Called when PiP animation is started. */ - public void onPipAnimationStart(IWindowContainer wc, PipTransitionAnimator animator) {} + public void onPipAnimationStart(PipTransitionAnimator animator) {} /** * Called when PiP animation is ended. */ - public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx, + public void onPipAnimationEnd(SurfaceControl.Transaction tx, PipTransitionAnimator animator) {} /** * Called when PiP animation is cancelled. */ - public void onPipAnimationCancel(IWindowContainer wc, PipTransitionAnimator animator) {} + public void onPipAnimationCancel(PipTransitionAnimator animator) {} } /** @@ -137,7 +134,6 @@ public class PipAnimationController { public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { - private final IWindowContainer mWindowContainer; private final boolean mScheduleFinishPip; private final SurfaceControl mLeash; private final @AnimationType int mAnimationType; @@ -149,23 +145,18 @@ public class PipAnimationController { private PipAnimationCallback mPipAnimationCallback; private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; - private PipTransitionAnimator(IWindowContainer wc, boolean scheduleFinishPip, + private PipTransitionAnimator(SurfaceControl leash, boolean scheduleFinishPip, @AnimationType int animationType, Rect destinationBounds, T startValue, T endValue) { - mWindowContainer = wc; mScheduleFinishPip = scheduleFinishPip; - try { - mLeash = wc.getLeash(); - mAnimationType = animationType; - mDestinationBounds.set(destinationBounds); - mStartValue = startValue; - mEndValue = endValue; - addListener(this); - addUpdateListener(this); - mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; - } catch (RemoteException e) { - throw new RuntimeException(e); - } + mLeash = leash; + mAnimationType = animationType; + mDestinationBounds.set(destinationBounds); + mStartValue = startValue; + mEndValue = endValue; + addListener(this); + addUpdateListener(this); + mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; } @Override @@ -173,7 +164,7 @@ public class PipAnimationController { mCurrentValue = mStartValue; applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), FRACTION_START); if (mPipAnimationCallback != null) { - mPipAnimationCallback.onPipAnimationStart(mWindowContainer, this); + mPipAnimationCallback.onPipAnimationStart(this); } } @@ -189,14 +180,14 @@ public class PipAnimationController { final SurfaceControl.Transaction tx = newSurfaceControlTransaction(); applySurfaceControlTransaction(mLeash, tx, FRACTION_END); if (mPipAnimationCallback != null) { - mPipAnimationCallback.onPipAnimationEnd(mWindowContainer, tx, this); + mPipAnimationCallback.onPipAnimationEnd(tx, this); } } @Override public void onAnimationCancel(Animator animation) { if (mPipAnimationCallback != null) { - mPipAnimationCallback.onPipAnimationCancel(mWindowContainer, this); + mPipAnimationCallback.onPipAnimationCancel(this); } } @@ -260,9 +251,9 @@ public class PipAnimationController { abstract void applySurfaceControlTransaction(SurfaceControl leash, SurfaceControl.Transaction tx, float fraction); - static PipTransitionAnimator<Float> ofAlpha(IWindowContainer wc, boolean scheduleFinishPip, + static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash, boolean scheduleFinishPip, Rect destinationBounds, float startValue, float endValue) { - return new PipTransitionAnimator<Float>(wc, scheduleFinishPip, ANIM_TYPE_ALPHA, + return new PipTransitionAnimator<Float>(leash, scheduleFinishPip, ANIM_TYPE_ALPHA, destinationBounds, startValue, endValue) { @Override void applySurfaceControlTransaction(SurfaceControl leash, @@ -281,10 +272,10 @@ public class PipAnimationController { }; } - static PipTransitionAnimator<Rect> ofBounds(IWindowContainer wc, boolean scheduleFinishPip, + static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash, boolean scheduleFinishPip, Rect startValue, Rect endValue) { // construct new Rect instances in case they are recycled - return new PipTransitionAnimator<Rect>(wc, scheduleFinishPip, ANIM_TYPE_BOUNDS, + return new PipTransitionAnimator<Rect>(leash, scheduleFinishPip, ANIM_TYPE_BOUNDS, endValue, new Rect(startValue), new Rect(endValue)) { private final Rect mTmpRect = new Rect(); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 836485a46e36..4766ebce1743 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -30,6 +30,7 @@ import android.content.Context; import android.graphics.Rect; import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.os.RemoteException; import android.util.Log; import android.view.DisplayInfo; @@ -38,9 +39,13 @@ import android.view.IWindowContainer; import android.view.SurfaceControl; import android.view.WindowContainerTransaction; +import com.android.internal.os.SomeArgs; +import com.android.systemui.pip.phone.PipUpdateThread; + import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; /** * Manages PiP tasks such as resize and offset. @@ -56,7 +61,13 @@ import java.util.Objects; public class PipTaskOrganizer extends ITaskOrganizer.Stub { private static final String TAG = PipTaskOrganizer.class.getSimpleName(); + private static final int MSG_RESIZE_IMMEDIATE = 1; + private static final int MSG_RESIZE_ANIMATE = 2; + private static final int MSG_OFFSET_ANIMATE = 3; + private static final int MSG_FINISH_RESIZE = 4; + private final Handler mMainHandler; + private final Handler mUpdateHandler; private final ITaskOrganizerController mTaskOrganizerController; private final PipBoundsHandler mPipBoundsHandler; private final PipAnimationController mPipAnimationController; @@ -64,11 +75,11 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { private final Rect mDisplayBounds = new Rect(); private final Rect mLastReportedBounds = new Rect(); + // These callbacks are called on the update thread private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = new PipAnimationController.PipAnimationCallback() { @Override - public void onPipAnimationStart(IWindowContainer wc, - PipAnimationController.PipTransitionAnimator animator) { + public void onPipAnimationStart(PipAnimationController.PipTransitionAnimator animator) { mMainHandler.post(() -> { for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); @@ -78,7 +89,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } @Override - public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx, + public void onPipAnimationEnd(SurfaceControl.Transaction tx, PipAnimationController.PipTransitionAnimator animator) { mMainHandler.post(() -> { for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { @@ -86,13 +97,11 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { callback.onPipTransitionFinished(); } }); - final Rect destinationBounds = animator.getDestinationBounds(); - finishResizeInternal(destinationBounds, wc, tx, animator.shouldScheduleFinishPip()); + finishResize(animator.getDestinationBounds(), tx, animator.shouldScheduleFinishPip()); } @Override - public void onPipAnimationCancel(IWindowContainer wc, - PipAnimationController.PipTransitionAnimator animator) { + public void onPipAnimationCancel(PipAnimationController.PipTransitionAnimator animator) { mMainHandler.post(() -> { for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); @@ -102,28 +111,75 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } }; + private Handler.Callback mUpdateCallbacks = new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + SomeArgs args = (SomeArgs) msg.obj; + Consumer<Rect> updateBoundsCallback = (Consumer<Rect>) args.arg1; + switch (msg.what) { + case MSG_RESIZE_IMMEDIATE: { + Rect toBounds = (Rect) args.arg2; + resizePip(toBounds); + if (updateBoundsCallback != null) { + updateBoundsCallback.accept(toBounds); + } + break; + } + case MSG_RESIZE_ANIMATE: { + Rect currentBounds = (Rect) args.arg2; + Rect toBounds = (Rect) args.arg3; + boolean scheduleFinishPip = args.argi1 != 0; + int duration = args.argi2; + animateResizePip(scheduleFinishPip, currentBounds, toBounds, duration); + if (updateBoundsCallback != null) { + updateBoundsCallback.accept(toBounds); + } + break; + } + case MSG_OFFSET_ANIMATE: { + Rect originalBounds = (Rect) args.arg2; + final int offset = args.argi1; + final int duration = args.argi2; + offsetPip(originalBounds, 0 /* xOffset */, offset, duration); + Rect toBounds = new Rect(originalBounds); + toBounds.offset(0, offset); + if (updateBoundsCallback != null) { + updateBoundsCallback.accept(toBounds); + } + break; + } + case MSG_FINISH_RESIZE: { + SurfaceControl.Transaction tx = (SurfaceControl.Transaction) args.arg2; + Rect toBounds = (Rect) args.arg3; + boolean scheduleFinishPip = args.argi1 != 0; + finishResize(toBounds, tx, scheduleFinishPip); + if (updateBoundsCallback != null) { + updateBoundsCallback.accept(toBounds); + } + break; + } + } + args.recycle(); + return true; + } + }; + private ActivityManager.RunningTaskInfo mTaskInfo; + private IWindowContainer mToken; + private SurfaceControl mLeash; + private boolean mInPip; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler) { mMainHandler = new Handler(Looper.getMainLooper()); + mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController(); mPipBoundsHandler = boundsHandler; mPipAnimationController = new PipAnimationController(context); } - /** - * Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE} - */ - public void offsetPinnedStack(Rect originalBounds, int xOffset, int yOffset, int durationMs) { - if (mTaskInfo == null) { - Log.w(TAG, "mTaskInfo is not set"); - return; - } - final Rect destinationBounds = new Rect(originalBounds); - destinationBounds.offset(xOffset, yOffset); - animateResizePipInternal(mTaskInfo.token, false /* scheduleFinishPip*/, - originalBounds, destinationBounds, durationMs); + public Handler getUpdateHandler() { + return mUpdateHandler; } /** @@ -171,7 +227,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { try { mLastReportedBounds.set(destinationBounds); final WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.setBounds(mTaskInfo.token, destinationBounds); + wct.setBounds(mToken, destinationBounds); mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */); } catch (RemoteException e) { Log.w(TAG, "Failed to apply window container transaction", e); @@ -185,13 +241,20 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); mTaskInfo = info; + mToken = mTaskInfo.token; + mInPip = true; + try { + mLeash = mToken.getLeash(); + } catch (RemoteException e) { + throw new RuntimeException("Unable to get leash", e); + } if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); - animateResizePipInternal(mTaskInfo.token, true /* scheduleFinishPip */, - currentBounds, destinationBounds, DURATION_DEFAULT_MS); + scheduleAnimateResizePip(true /* scheduleFinishPip */, + currentBounds, destinationBounds, DURATION_DEFAULT_MS, null); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { - mMainHandler.post(() -> mPipAnimationController - .getAnimator(mTaskInfo.token, true /* scheduleFinishPip */, + mUpdateHandler.post(() -> mPipAnimationController + .getAnimator(mLeash, true /* scheduleFinishPip */, destinationBounds, 0f, 1f) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(DURATION_DEFAULT_MS) @@ -205,12 +268,12 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { @Override public void taskVanished(IWindowContainer token) { Objects.requireNonNull(token, "Requires valid IWindowContainer"); - if (token.asBinder() != mTaskInfo.token.asBinder()) { + if (token.asBinder() != mToken.asBinder()) { Log.wtf(TAG, "Unrecognized token: " + token); return; } - animateResizePipInternal(token, false /* scheduleFinishPip */, - mLastReportedBounds, mDisplayBounds, DURATION_DEFAULT_MS); + scheduleAnimateResizePip(mDisplayBounds, DURATION_DEFAULT_MS, null); + mInPip = false; } @Override @@ -227,7 +290,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( getAspectRatioOrDefault(newParams), null /* bounds */); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); - animateResizePip(destinationBounds, DURATION_DEFAULT_MS); + scheduleAnimateResizePip(destinationBounds, DURATION_DEFAULT_MS, null); } /** @@ -243,102 +306,158 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } /** - * Directly perform manipulation/resize on the leash. This will not perform any - * {@link WindowContainerTransaction} until {@link #finishResize} is called. + * Animates resizing of the pinned stack given the duration. */ - public void resizePip(Rect destinationBounds) { - Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer"); - resizePipInternal(mTaskInfo.token, destinationBounds); + public void scheduleAnimateResizePip(Rect toBounds, int duration, + Consumer<Rect> updateBoundsCallback) { + scheduleAnimateResizePip(false /* scheduleFinishPip */, + mLastReportedBounds, toBounds, duration, updateBoundsCallback); } - private void resizePipInternal(IWindowContainer wc, - Rect destinationBounds) { - Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer"); - try { - // Could happen when dismissPip - if (wc == null || wc.getLeash() == null) { - Log.w(TAG, "Abort animation, invalid leash"); - return; - } - final SurfaceControl leash = wc.getLeash(); - new SurfaceControl.Transaction() - .setPosition(leash, destinationBounds.left, destinationBounds.top) - .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height()) - .apply(); - } catch (RemoteException e) { - Log.w(TAG, "Abort animation, invalid window container", e); - } catch (Exception e) { - Log.e(TAG, "Should not reach here, terrible thing happened", e); + private void scheduleAnimateResizePip(boolean scheduleFinishPip, + Rect currentBounds, Rect destinationBounds, int durationMs, + Consumer<Rect> updateBoundsCallback) { + Objects.requireNonNull(mToken, "Requires valid IWindowContainer"); + if (!mInPip) { + // Ignore animation when we are no longer in PIP + return; } + SomeArgs args = SomeArgs.obtain(); + args.arg1 = updateBoundsCallback; + args.arg2 = currentBounds; + args.arg3 = destinationBounds; + args.argi1 = scheduleFinishPip ? 1 : 0; + args.argi2 = durationMs; + mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args)); + } + + /** + * Directly perform manipulation/resize on the leash. This will not perform any + * {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called. + */ + public void scheduleResizePip(Rect toBounds, Consumer<Rect> updateBoundsCallback) { + Objects.requireNonNull(mToken, "Requires valid IWindowContainer"); + SomeArgs args = SomeArgs.obtain(); + args.arg1 = updateBoundsCallback; + args.arg2 = toBounds; + mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args)); } /** * Finish a intermediate resize operation. This is expected to be called after - * {@link #resizePip}. + * {@link #scheduleResizePip}. */ - public void finishResize(Rect destinationBounds) { - try { - final IWindowContainer wc = mTaskInfo.token; - SurfaceControl.Transaction tx = new SurfaceControl.Transaction() - .setPosition(wc.getLeash(), destinationBounds.left, - destinationBounds.top) - .setWindowCrop(wc.getLeash(), destinationBounds.width(), - destinationBounds.height()); - finishResizeInternal(destinationBounds, wc, tx, false); - } catch (RemoteException e) { - Log.e(TAG, "Failed to obtain leash"); + public void scheduleFinishResizePip(Rect destinationBounds) { + Objects.requireNonNull(mToken, "Requires valid IWindowContainer"); + SurfaceControl.Transaction tx = new SurfaceControl.Transaction() + .setPosition(mLeash, destinationBounds.left, destinationBounds.top) + .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); + scheduleFinishResizePip(tx, destinationBounds, false /* scheduleFinishPip */, + null); + } + + private void scheduleFinishResizePip(SurfaceControl.Transaction tx, + Rect destinationBounds, boolean scheduleFinishPip, + Consumer<Rect> updateBoundsCallback) { + Objects.requireNonNull(mToken, "Requires valid IWindowContainer"); + SomeArgs args = SomeArgs.obtain(); + args.arg1 = updateBoundsCallback; + args.arg2 = tx; + args.arg3 = destinationBounds; + args.argi1 = scheduleFinishPip ? 1 : 0; + mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_FINISH_RESIZE, args)); + } + + /** + * Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE} + */ + public void scheduleOffsetPip(Rect originalBounds, int offset, int duration, + Consumer<Rect> updateBoundsCallback) { + if (!mInPip) { + // Ignore offsets when we are no longer in PIP + return; } + Objects.requireNonNull(mToken, "Requires valid IWindowContainer"); + SomeArgs args = SomeArgs.obtain(); + args.arg1 = updateBoundsCallback; + args.arg2 = originalBounds; + // offset would be zero if triggered from screen rotation. + args.argi1 = offset; + args.argi2 = duration; + mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args)); } - private void finishResizeInternal(Rect destinationBounds, IWindowContainer wc, - SurfaceControl.Transaction tx, boolean shouldScheduleFinishPip) { + private void offsetPip(Rect originalBounds, int xOffset, int yOffset, int durationMs) { + if (Looper.myLooper() != mUpdateHandler.getLooper()) { + throw new RuntimeException("Callers should call scheduleOffsetPip() instead of this " + + "directly"); + } + if (mTaskInfo == null) { + Log.w(TAG, "mTaskInfo is not set"); + return; + } + final Rect destinationBounds = new Rect(originalBounds); + destinationBounds.offset(xOffset, yOffset); + animateResizePip(false /* scheduleFinishPip*/, originalBounds, destinationBounds, + durationMs); + } + + private void resizePip(Rect destinationBounds) { + if (Looper.myLooper() != mUpdateHandler.getLooper()) { + throw new RuntimeException("Callers should call scheduleResizePip() instead of this " + + "directly"); + } + Objects.requireNonNull(mToken, "Requires valid IWindowContainer"); + // Could happen when dismissPip + if (mToken == null || mLeash == null) { + Log.w(TAG, "Abort animation, invalid leash"); + return; + } + new SurfaceControl.Transaction() + .setPosition(mLeash, destinationBounds.left, destinationBounds.top) + .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()) + .apply(); + } + + private void finishResize(Rect destinationBounds, SurfaceControl.Transaction tx, + boolean shouldScheduleFinishPip) { + if (Looper.myLooper() != mUpdateHandler.getLooper()) { + throw new RuntimeException("Callers should call scheduleResizePip() instead of this " + + "directly"); + } mLastReportedBounds.set(destinationBounds); try { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (shouldScheduleFinishPip) { - wct.scheduleFinishEnterPip(wc, destinationBounds); + wct.scheduleFinishEnterPip(mToken, destinationBounds); } else { - wct.setBounds(wc, destinationBounds); + wct.setBounds(mToken, destinationBounds); } - wct.setBoundsChangeTransaction(mTaskInfo.token, tx); + wct.setBoundsChangeTransaction(mToken, tx); mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */); } catch (RemoteException e) { Log.e(TAG, "Failed to apply container transaction", e); } } - /** - * Animates resizing of the pinned stack given the duration. - */ - public void animateResizePip(Rect destinationBounds, int durationMs) { - Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer"); - animateResizePipInternal(mTaskInfo.token, false, mLastReportedBounds, - destinationBounds, durationMs); - } - - private void animateResizePipInternal(IWindowContainer wc, boolean scheduleFinishPip, - Rect currentBounds, Rect destinationBounds, int durationMs) { - try { - // Could happen when dismissPip - if (wc == null || wc.getLeash() == null) { - Log.w(TAG, "Abort animation, invalid leash"); - return; - } - final SurfaceControl leash = wc.getLeash(); - - mMainHandler.post(() -> mPipAnimationController - .getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds) - .setPipAnimationCallback(mPipAnimationCallback) - .setDuration(durationMs) - .start()); - } catch (RemoteException e) { - Log.w(TAG, "Abort animation, invalid window container", e); - } catch (Exception e) { - Log.e(TAG, "Should not reach here, terrible thing happened", e); + private void animateResizePip(boolean scheduleFinishPip, Rect currentBounds, + Rect destinationBounds, int durationMs) { + if (Looper.myLooper() != mUpdateHandler.getLooper()) { + throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of " + + "this directly"); + } + // Could happen when dismissPip + if (mToken == null || mLeash == null) { + Log.w(TAG, "Abort animation, invalid leash"); + return; } + mUpdateHandler.post(() -> mPipAnimationController + .getAnimator(mLeash, scheduleFinishPip, currentBounds, destinationBounds) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(durationMs) + .start()); } - private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) { return params == null ? mPipBoundsHandler.getDefaultAspectRatio() 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 81e8a0b91211..fc04f795c056 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -60,6 +60,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.util.Pair; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -226,6 +227,15 @@ public class PipMenuActivity extends Activity { } @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_ESCAPE) { + hideMenu(); + return true; + } + return super.onKeyUp(keyCode, event); + } + + @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); updateFromIntent(intent); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index fdaf66a2b053..33760be3f469 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -28,16 +28,11 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Debug; -import android.os.Handler; -import android.os.Message; import android.os.RemoteException; import android.util.Log; -import android.view.Choreographer; import androidx.dynamicanimation.animation.SpringForce; -import com.android.internal.graphics.SfVsyncFrameCallbackProvider; -import com.android.internal.os.SomeArgs; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -47,11 +42,12 @@ import com.android.systemui.util.animation.FloatProperties; import com.android.systemui.util.animation.PhysicsAnimator; import java.io.PrintWriter; +import java.util.function.Consumer; /** * A helper to animate and manipulate the PiP. */ -public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Callback, +public class PipMotionHelper implements PipAppOpsListener.Callback, FloatingContentCoordinator.FloatingContent { private static final String TAG = "PipMotionHelper"; @@ -68,14 +64,9 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f; - private static final int MSG_RESIZE_IMMEDIATE = 1; - private static final int MSG_RESIZE_ANIMATE = 2; - private static final int MSG_OFFSET_ANIMATE = 3; - private final Context mContext; private final IActivityTaskManager mActivityTaskManager; private final PipTaskOrganizer mPipTaskOrganizer; - private final Handler mHandler; private PipMenuActivityController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; @@ -92,9 +83,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call /** The region that all of PIP must stay within. */ private Rect mFloatingAllowedArea = new Rect(); - private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider = - new SfVsyncFrameCallbackProvider(); - /** * Bounds that are animated using the physics animator. */ @@ -112,16 +100,11 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call private PhysicsAnimator<Rect> mAnimatedBoundsPhysicsAnimator = PhysicsAnimator.getInstance( mAnimatedBounds); - /** Callback that re-sizes PIP to the animated bounds. */ - private final Choreographer.FrameCallback mResizePipVsyncCallback = - l -> resizePipUnchecked(mAnimatedBounds); - /** - * Update listener that posts a vsync frame callback to resize PIP to {@link #mAnimatedBounds}. + * Update listener that resizes the PIP to {@link #mAnimatedBounds}. */ - private final PhysicsAnimator.UpdateListener<Rect> mResizePipVsyncUpdateListener = - (target, values) -> - mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback); + private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener = + (target, values) -> resizePipUnchecked(mAnimatedBounds); /** FlingConfig instances provided to PhysicsAnimator for fling gestures. */ private PhysicsAnimator.FlingConfig mFlingConfigX; @@ -137,12 +120,13 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call new PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); + private final Consumer<Rect> mUpdateBoundsCallback = (toBounds) -> mBounds.set(toBounds); + public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager, PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils, FloatingContentCoordinator floatingContentCoordinator) { mContext = context; - mHandler = new Handler(ForegroundThread.get().getLooper(), this); mActivityTaskManager = activityTaskManager; mPipTaskOrganizer = pipTaskOrganizer; mMenuController = menuController; @@ -234,7 +218,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } cancelAnimations(); mMenuController.hideMenuWithoutResize(); - mHandler.post(() -> { + mPipTaskOrganizer.getUpdateHandler().post(() -> { try { mActivityTaskManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION); } catch (RemoteException e) { @@ -253,7 +237,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } cancelAnimations(); mMenuController.hideMenuWithoutResize(); - mHandler.post(() -> { + mPipTaskOrganizer.getUpdateHandler().post(() -> { try { mActivityTaskManager.removeStacksInWindowingModes( new int[]{ WINDOWING_MODE_PINNED }); @@ -406,17 +390,13 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call * Animates the PiP to offset it from the IME or shelf. */ void animateToOffset(Rect originalBounds, int offset) { + if (DEBUG) { + Log.d(TAG, "animateToOffset: originalBounds=" + originalBounds + " offset=" + offset + + " callers=\n" + Debug.getCallers(5, " ")); + } cancelAnimations(); - adjustAndAnimatePipOffset(originalBounds, offset, SHIFT_DURATION); - } - - private void adjustAndAnimatePipOffset(Rect originalBounds, int offset, int duration) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = originalBounds; - // offset would be zero if triggered from screen rotation. - args.argi1 = offset; - args.argi2 = duration; - mHandler.sendMessage(mHandler.obtainMessage(MSG_OFFSET_ANIMATE, args)); + mPipTaskOrganizer.scheduleOffsetPip(originalBounds, offset, SHIFT_DURATION, + mUpdateBoundsCallback); } /** @@ -437,8 +417,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call /** * Starts the physics animator which will update the animated PIP bounds using physics - * animations, as well as the TimeAnimator which will apply those bounds to PIP at intervals - * synchronized with the SurfaceFlinger vsync frame provider. + * animations, as well as the TimeAnimator which will apply those bounds to PIP. * * This will also add end actions to the bounds animator that cancel the TimeAnimator and update * the 'real' bounds to equal the final animated bounds. @@ -448,7 +427,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call mAnimatedBoundsPhysicsAnimator .withEndActions(() -> mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds)) - .addUpdateListener(mResizePipVsyncUpdateListener) + .addUpdateListener(mResizePipUpdateListener) .start(); } @@ -471,9 +450,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call + " callers=\n" + Debug.getCallers(5, " ")); } if (!toBounds.equals(mBounds)) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = toBounds; - mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args)); + mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback); } } @@ -486,10 +463,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call + " duration=" + duration + " callers=\n" + Debug.getCallers(5, " ")); } if (!toBounds.equals(mBounds)) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = toBounds; - args.argi1 = duration; - mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_ANIMATE, args)); + mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, mUpdateBoundsCallback); setAnimatingToBounds(toBounds); } } @@ -538,70 +512,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return dismissArea.contains(endpoint.x, endpoint.y); } - /** - * Handles messages to be processed on the background thread. - */ - public boolean handleMessage(Message msg) { - switch (msg.what) { - case MSG_RESIZE_IMMEDIATE: { - SomeArgs args = (SomeArgs) msg.obj; - Rect toBounds = (Rect) args.arg1; - mPipTaskOrganizer.resizePip(toBounds); - mBounds.set(toBounds); - return true; - } - - case MSG_RESIZE_ANIMATE: { - SomeArgs args = (SomeArgs) msg.obj; - Rect toBounds = (Rect) args.arg1; - int duration = args.argi1; - try { - StackInfo stackInfo = mActivityTaskManager.getStackInfo( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); - if (stackInfo == null) { - // In the case where we've already re-expanded or dismissed the PiP, then - // just skip the resize - return true; - } - - mPipTaskOrganizer.animateResizePip(toBounds, duration); - mBounds.set(toBounds); - } catch (RemoteException e) { - Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e); - } - return true; - } - - case MSG_OFFSET_ANIMATE: { - SomeArgs args = (SomeArgs) msg.obj; - Rect originalBounds = (Rect) args.arg1; - final int offset = args.argi1; - final int duration = args.argi2; - try { - StackInfo stackInfo = mActivityTaskManager.getStackInfo( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); - if (stackInfo == null) { - // In the case where we've already re-expanded or dismissed the PiP, then - // just skip the resize - return true; - } - - mPipTaskOrganizer.offsetPinnedStack(originalBounds, - 0 /* xOffset */, offset, duration); - Rect toBounds = new Rect(originalBounds); - toBounds.offset(0, offset); - mBounds.set(toBounds); - } catch (RemoteException e) { - Log.e(TAG, "Could not animate offset pinned stack with offset: " + offset, e); - } - return true; - } - - default: - return false; - } - } - public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/ForegroundThread.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUpdateThread.java index 9bf46bb488f3..6c5d84645e92 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/ForegroundThread.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUpdateThread.java @@ -21,33 +21,38 @@ import android.os.HandlerThread; /** * Similar to {@link com.android.internal.os.BackgroundThread}, this is a shared singleton - * foreground thread for each process. + * foreground thread for each process for updating PIP. */ -public final class ForegroundThread extends HandlerThread { - private static ForegroundThread sInstance; +public final class PipUpdateThread extends HandlerThread { + private static PipUpdateThread sInstance; private static Handler sHandler; - private ForegroundThread() { - super("recents.fg"); + private PipUpdateThread() { + super("pip"); } private static void ensureThreadLocked() { if (sInstance == null) { - sInstance = new ForegroundThread(); + sInstance = new PipUpdateThread(); sInstance.start(); sHandler = new Handler(sInstance.getLooper()); } } - public static ForegroundThread get() { - synchronized (ForegroundThread.class) { + /** + * @return the static update thread instance + */ + public static PipUpdateThread get() { + synchronized (PipUpdateThread.class) { ensureThreadLocked(); return sInstance; } } - + /** + * @return the static update thread handler instance + */ public static Handler getHandler() { - synchronized (ForegroundThread.class) { + synchronized (PipUpdateThread.class) { ensureThreadLocked(); return sHandler; } 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 ca44f6b364a7..a5e9dbc41924 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -20,6 +20,8 @@ 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; @@ -50,7 +52,6 @@ import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.pip.BasePipManager; -import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -233,6 +234,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio if (mInitialized) { return; } + mInitialized = true; mContext = context; mPipBoundsHandler = pipBoundsHandler; @@ -434,8 +436,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mCurrentPipBounds = mPipBounds; break; } - mPipTaskOrganizer.animateResizePip(mCurrentPipBounds, - PipAnimationController.DURATION_DEFAULT_MS); + mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, DURATION_DEFAULT_MS, null); } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 37a8ca5c42c3..8b8b6f8071e1 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -16,10 +16,6 @@ package com.android.systemui.screenshot; -import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI; - -import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_CORNER_FLOW; - import android.app.Service; import android.content.Intent; import android.graphics.Bitmap; @@ -33,7 +29,6 @@ import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.os.UserManager; -import android.provider.DeviceConfig; import android.util.Log; import android.view.WindowManager; @@ -70,8 +65,7 @@ public class TakeScreenshotService extends Service { } // TODO (mkephart): clean up once notifications flow is fully deprecated - boolean useCornerFlow = DeviceConfig.getBoolean( - NAMESPACE_SYSTEMUI, SCREENSHOT_CORNER_FLOW, true); + boolean useCornerFlow = true; switch (msg.what) { case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: if (useCornerFlow) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index 333fa3ca0a2d..c6eecf260dac 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -17,6 +17,8 @@ package com.android.systemui.stackdivider; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; +import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import android.animation.Animator; @@ -214,8 +216,22 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, if (mTargetAdjusted) { mSplitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop); wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary); + // "Freeze" the configuration size so that the app doesn't get a config + // or relaunch. This is required because normally nav-bar contributes + // to configuration bounds (via nondecorframe). + Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration + .windowConfiguration.getAppBounds()); + adjustAppBounds.offset(0, mSplitLayout.mAdjustedSecondary.top + - mSplitLayout.mSecondary.top); + wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds); + wct.setScreenSizeDp(mSplits.mSecondary.token, + mSplits.mSecondary.configuration.screenWidthDp, + mSplits.mSecondary.configuration.screenHeightDp); } else { wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary); + wct.setAppBounds(mSplits.mSecondary.token, null); + wct.setScreenSizeDp(mSplits.mSecondary.token, + SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); } try { ActivityTaskManager.getTaskOrganizerController() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index d746822ddcff..bd4984e8d24e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -315,7 +315,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(), row); }; - boolean isForBlockingHelper = row.isBlockingHelperShowing(); if (!userHandle.equals(UserHandle.ALL) || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) { @@ -335,13 +334,10 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx row.getEntry().getChannel(), row.getUniqueChannels(), row.getEntry(), - mCheckSaveListener, onSettingsClick, onAppSettingsClick, mDeviceProvisionedController.isDeviceProvisioned(), row.getIsNonblockable(), - isForBlockingHelper, - row.getEntry().getImportance(), mHighPriorityProvider.isHighPriority(row.getEntry())); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 6b4511d31669..12aa4dfaf6fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -24,10 +24,6 @@ import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; import static java.lang.annotation.RetentionPolicy.SOURCE; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.annotation.IntDef; import android.annotation.Nullable; import android.app.INotificationManager; @@ -53,7 +49,6 @@ import android.transition.TransitionSet; import android.util.AttributeSet; import android.util.Log; import android.view.View; -import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; import android.widget.LinearLayout; @@ -63,42 +58,33 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; -import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.logging.NotificationCounters; import java.lang.annotation.Retention; import java.util.List; import java.util.Set; /** - * The guts of a notification revealed when performing a long press. This also houses the blocking - * helper affordance that allows a user to keep/stop notifications after swiping one away. + * The guts of a notification revealed when performing a long press. */ public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent { private static final String TAG = "InfoGuts"; @IntDef(prefix = { "ACTION_" }, value = { ACTION_NONE, - ACTION_UNDO, + ACTION_TOGGLE_ALERT, ACTION_TOGGLE_SILENT, - ACTION_BLOCK, }) public @interface NotificationInfoAction { } public static final int ACTION_NONE = 0; - static final int ACTION_UNDO = 1; // standard controls static final int ACTION_TOGGLE_SILENT = 2; - // unused - static final int ACTION_BLOCK = 3; - // blocking helper - static final int ACTION_DELIVER_SILENTLY = 4; // standard controls - private static final int ACTION_ALERT = 5; + private static final int ACTION_TOGGLE_ALERT = 5; private TextView mPriorityDescriptionView; private TextView mSilentDescriptionView; @@ -128,101 +114,35 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G @Nullable private Integer mChosenImportance; private boolean mIsSingleDefaultChannel; private boolean mIsNonblockable; - private NotificationEntry mEntry; private StatusBarNotification mSbn; - private AnimatorSet mExpandAnimation; private boolean mIsDeviceProvisioned; - private CheckSaveListener mCheckSaveListener; private OnSettingsClickListener mOnSettingsClickListener; private OnAppSettingsClickListener mAppSettingsClickListener; private NotificationGuts mGutsContainer; private Drawable mPkgIcon; - /** Whether this view is being shown as part of the blocking helper. */ - private boolean mIsForBlockingHelper; - @VisibleForTesting boolean mSkipPost = false; - /** - * String that describes how the user exit or quit out of this view, also used as a counter tag. - */ - private String mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED; - - // used by standard ui private OnClickListener mOnAlert = v -> { - mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING; mChosenImportance = IMPORTANCE_DEFAULT; applyAlertingBehavior(BEHAVIOR_ALERTING, true /* userTriggered */); }; // used by standard ui private OnClickListener mOnSilent = v -> { - mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY; mChosenImportance = IMPORTANCE_LOW; applyAlertingBehavior(BEHAVIOR_SILENT, true /* userTriggered */); }; - // used by standard ui private OnClickListener mOnDismissSettings = v -> { mPressedApply = true; closeControls(v, true); }; - // used by blocking helper - private OnClickListener mOnKeepShowing = v -> { - mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING; - closeControls(v, true); - mMetricsLogger.write(getLogMaker().setCategory( - MetricsEvent.NOTIFICATION_BLOCKING_HELPER) - .setType(MetricsEvent.TYPE_ACTION) - .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT)); - }; - - // used by blocking helper - private OnClickListener mOnDeliverSilently = v -> { - handleSaveImportance( - ACTION_DELIVER_SILENTLY, MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT); - }; - - private void handleSaveImportance(int action, int metricsSubtype) { - Runnable saveImportance = () -> { - saveImportanceAndExitReason(action); - if (mIsForBlockingHelper) { - swapContent(action, true /* animate */); - mMetricsLogger.write(getLogMaker() - .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER) - .setType(MetricsEvent.TYPE_ACTION) - .setSubtype(metricsSubtype)); - } - }; - if (mCheckSaveListener != null) { - mCheckSaveListener.checkSave(saveImportance, mSbn); - } else { - saveImportance.run(); - } - } - - private OnClickListener mOnUndo = v -> { - // Reset exit counter that we'll log and record an undo event separately (not an exit event) - mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED; - if (mIsForBlockingHelper) { - logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO); - mMetricsLogger.write(getLogMaker().setCategory( - MetricsEvent.NOTIFICATION_BLOCKING_HELPER) - .setType(MetricsEvent.TYPE_DISMISS) - .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_UNDO)); - } else { - // TODO: this can't happen? - mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS)); - } - saveImportanceAndExitReason(ACTION_UNDO); - swapContent(ACTION_UNDO, true /* animate */); - }; - public NotificationInfo(Context context, AttributeSet attrs) { super(context, attrs); } @@ -250,30 +170,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G void onClick(View v, Intent intent); } - @VisibleForTesting - void bindNotification( - final PackageManager pm, - final INotificationManager iNotificationManager, - final VisualStabilityManager visualStabilityManager, - final String pkg, - final NotificationChannel notificationChannel, - final Set<NotificationChannel> uniqueChannelsInRow, - final NotificationEntry entry, - final CheckSaveListener checkSaveListener, - final OnSettingsClickListener onSettingsClick, - final OnAppSettingsClickListener onAppSettingsClick, - boolean isDeviceProvisioned, - boolean isNonblockable, - int importance, - boolean wasShownHighPriority) - throws RemoteException { - bindNotification(pm, iNotificationManager, visualStabilityManager, pkg, notificationChannel, - uniqueChannelsInRow, entry, checkSaveListener, onSettingsClick, - onAppSettingsClick, isDeviceProvisioned, isNonblockable, - false /* isBlockingHelper */, - importance, wasShownHighPriority); - } - public void bindNotification( PackageManager pm, INotificationManager iNotificationManager, @@ -282,13 +178,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G NotificationChannel notificationChannel, Set<NotificationChannel> uniqueChannelsInRow, NotificationEntry entry, - CheckSaveListener checkSaveListener, OnSettingsClickListener onSettingsClick, OnAppSettingsClickListener onAppSettingsClick, boolean isDeviceProvisioned, boolean isNonblockable, - boolean isForBlockingHelper, - int importance, boolean wasShownHighPriority) throws RemoteException { mINotificationManager = iNotificationManager; @@ -298,18 +191,15 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mPackageName = pkg; mUniqueChannelsInRow = uniqueChannelsInRow; mNumUniqueChannelsInRow = uniqueChannelsInRow.size(); - mEntry = entry; mSbn = entry.getSbn(); mPm = pm; mAppSettingsClickListener = onAppSettingsClick; mAppName = mPackageName; - mCheckSaveListener = checkSaveListener; mOnSettingsClickListener = onSettingsClick; mSingleNotificationChannel = notificationChannel; mStartingChannelImportance = mSingleNotificationChannel.getImportance(); mWasShownHighPriority = wasShownHighPriority; mIsNonblockable = isNonblockable; - mIsForBlockingHelper = isForBlockingHelper; mAppUid = mSbn.getUid(); mDelegatePkg = mSbn.getOpPkg(); mIsDeviceProvisioned = isDeviceProvisioned; @@ -329,36 +219,12 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G bindHeader(); bindChannelDetails(); - if (mIsForBlockingHelper) { - bindBlockingHelper(); - } else { - bindInlineControls(); - } + bindInlineControls(); mMetricsLogger.write(notificationControlsLogMaker()); } - private void bindBlockingHelper() { - findViewById(R.id.inline_controls).setVisibility(GONE); - findViewById(R.id.blocking_helper).setVisibility(VISIBLE); - - findViewById(R.id.undo).setOnClickListener(mOnUndo); - - View turnOffButton = findViewById(R.id.blocking_helper_turn_off_notifications); - turnOffButton.setOnClickListener(getSettingsOnClickListener()); - turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() ? VISIBLE : GONE); - - TextView keepShowing = findViewById(R.id.keep_showing); - keepShowing.setOnClickListener(mOnKeepShowing); - - View deliverSilently = findViewById(R.id.deliver_silently); - deliverSilently.setOnClickListener(mOnDeliverSilently); - } - private void bindInlineControls() { - findViewById(R.id.inline_controls).setVisibility(VISIBLE); - findViewById(R.id.blocking_helper).setVisibility(GONE); - if (mIsNonblockable) { findViewById(R.id.non_configurable_text).setVisibility(VISIBLE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); @@ -414,8 +280,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G // app is gone, just show package name and generic icon mPkgIcon = mPm.getDefaultActivityIcon(); } - ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(mPkgIcon); - ((TextView) findViewById(R.id.pkgname)).setText(mAppName); + ((ImageView) findViewById(R.id.pkg_icon)).setImageDrawable(mPkgIcon); + ((TextView) findViewById(R.id.pkg_name)).setText(mAppName); // Delegate bindDelegate(); @@ -445,8 +311,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) { final int appUidF = mAppUid; return ((View view) -> { - logBlockingHelperCounter( - NotificationCounters.BLOCKING_HELPER_NOTIF_SETTINGS); mOnSettingsClickListener.onClick(view, mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel, appUidF); @@ -487,16 +351,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private void bindDelegate() { TextView delegateView = findViewById(R.id.delegate_name); - TextView dividerView = findViewById(R.id.pkg_divider); CharSequence delegatePkg = null; if (!TextUtils.equals(mPackageName, mDelegatePkg)) { // this notification was posted by a delegate! delegateView.setVisibility(View.VISIBLE); - dividerView.setVisibility(View.VISIBLE); } else { delegateView.setVisibility(View.GONE); - dividerView.setVisibility(View.GONE); } } @@ -512,25 +373,19 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } TextView groupNameView = findViewById(R.id.group_name); + View divider = findViewById(R.id.group_divider); if (groupName != null) { groupNameView.setText(groupName); - groupNameView.setVisibility(View.VISIBLE); + groupNameView.setVisibility(VISIBLE); + divider.setVisibility(VISIBLE); } else { - groupNameView.setVisibility(View.GONE); - } - } - - - @VisibleForTesting - void logBlockingHelperCounter(String counterTag) { - if (mIsForBlockingHelper) { - mMetricsLogger.count(counterTag, 1); + groupNameView.setVisibility(GONE); + divider.setVisibility(GONE); } } private void saveImportance() { - if (!mIsNonblockable - || mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) { + if (!mIsNonblockable) { if (mChosenImportance == null) { mChosenImportance = mStartingChannelImportance; } @@ -621,99 +476,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G : R.string.inline_done_button); } - private void saveImportanceAndExitReason(@NotificationInfoAction int action) { - switch (action) { - case ACTION_UNDO: - mChosenImportance = mStartingChannelImportance; - break; - case ACTION_DELIVER_SILENTLY: - mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY; - mChosenImportance = mWasShownHighPriority - ? IMPORTANCE_LOW : mStartingChannelImportance; - break; - default: - throw new IllegalArgumentException(); - } - } - - // only used for blocking helper - private void swapContent(@NotificationInfoAction int action, boolean animate) { - if (mExpandAnimation != null) { - mExpandAnimation.cancel(); - } - - View blockingHelper = findViewById(R.id.blocking_helper); - ViewGroup confirmation = findViewById(R.id.confirmation); - TextView confirmationText = findViewById(R.id.confirmation_text); - - saveImportanceAndExitReason(action); - - switch (action) { - case ACTION_UNDO: - break; - case ACTION_DELIVER_SILENTLY: - confirmationText.setText(R.string.notification_channel_silenced); - break; - default: - throw new IllegalArgumentException(); - } - - boolean isUndo = action == ACTION_UNDO; - - blockingHelper.setVisibility(isUndo ? VISIBLE : GONE); - findViewById(R.id.channel_info).setVisibility(isUndo ? VISIBLE : GONE); - findViewById(R.id.header).setVisibility(isUndo ? VISIBLE : GONE); - confirmation.setVisibility(isUndo ? GONE : VISIBLE); - - if (animate) { - ObjectAnimator promptAnim = ObjectAnimator.ofFloat(blockingHelper, View.ALPHA, - blockingHelper.getAlpha(), isUndo ? 1f : 0f); - promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT); - ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA, - confirmation.getAlpha(), isUndo ? 0f : 1f); - confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN); - - mExpandAnimation = new AnimatorSet(); - mExpandAnimation.playTogether(promptAnim, confirmAnim); - mExpandAnimation.setDuration(150); - mExpandAnimation.addListener(new AnimatorListenerAdapter() { - boolean mCancelled = false; - - @Override - public void onAnimationCancel(Animator animation) { - mCancelled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (!mCancelled) { - blockingHelper.setVisibility(isUndo ? VISIBLE : GONE); - confirmation.setVisibility(isUndo ? GONE : VISIBLE); - } - } - }); - mExpandAnimation.start(); - } - - // Since we're swapping/update the content, reset the timeout so the UI can't close - // immediately after the update. - if (mGutsContainer != null) { - mGutsContainer.resetFalsingCheck(); - } - } - @Override public void onFinishedClosing() { if (mChosenImportance != null) { mStartingChannelImportance = mChosenImportance; } - mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED; - if (mIsForBlockingHelper) { - bindBlockingHelper(); - } else { - bindInlineControls(); - } + bindInlineControls(); mMetricsLogger.write(notificationControlsLogMaker().setType(MetricsEvent.TYPE_CLOSE)); } @@ -756,13 +525,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } /** - * Closes the controls and commits the updated importance values (indirectly). If this view is - * being used to show the blocking helper, this will immediately dismiss the blocking helper and - * commit the updated importance. + * Closes the controls and commits the updated importance values (indirectly). * * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the - * user does not have the ability to undo the action anymore. See - * {@link #swapContent(boolean, boolean)} for where undo is handled. + * user does not have the ability to undo the action anymore. */ @VisibleForTesting void closeControls(View v, boolean save) { @@ -811,7 +577,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G if (save) { saveImportance(); } - logBlockingHelperCounter(mExitReason); return false; } @@ -822,7 +587,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G @VisibleForTesting public boolean isAnimating() { - return mExpandAnimation != null && mExpandAnimation.isRunning(); + return false; } /** @@ -901,8 +666,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private LogMaker notificationControlsLogMaker() { return getLogMaker().setCategory(MetricsEvent.ACTION_NOTE_CONTROLS) .setType(MetricsEvent.TYPE_OPEN) - .setSubtype(mIsForBlockingHelper ? MetricsEvent.BLOCKING_HELPER_DISPLAY - : MetricsEvent.BLOCKING_HELPER_UNKNOWN); + .setSubtype(MetricsEvent.BLOCKING_HELPER_UNKNOWN); } @Retention(SOURCE) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationUndoLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationUndoLayout.java deleted file mode 100644 index 3ea8195ff917..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationUndoLayout.java +++ /dev/null @@ -1,139 +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.statusbar.notification.row; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; - -import com.android.systemui.R; - -/** - * Custom view for the NotificationInfo confirmation views so that the confirmation text can - * occupy the full width of the notification and push the undo button down to the next line if - * necessary. - * - * @see NotificationInfo - */ -public class NotificationUndoLayout extends FrameLayout { - /** - * View for the prompt/confirmation text to tell the user the previous action was successful. - */ - private View mConfirmationTextView; - /** Undo button (actionable text) view. */ - private View mUndoView; - - /** - * Whether {@link #mConfirmationTextView} is multiline and will require the full width of the - * parent (which causes the {@link #mUndoView} to push down). - */ - private boolean mIsMultiline = false; - private int mMultilineTopMargin; - - public NotificationUndoLayout(Context context) { - this(context, null); - } - - public NotificationUndoLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public NotificationUndoLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - mConfirmationTextView = findViewById(R.id.confirmation_text); - mUndoView = findViewById(R.id.undo); - - mMultilineTopMargin = getResources().getDimensionPixelOffset( - com.android.internal.R.dimen.notification_content_margin_start); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - LayoutParams confirmationLayoutParams = - (LayoutParams) mConfirmationTextView.getLayoutParams(); - LayoutParams undoLayoutParams =(LayoutParams) mUndoView.getLayoutParams(); - - int measuredWidth = getMeasuredWidth(); - // Ignore the left margin on the undo button - no need for additional extra space between - // the text and the button. - int requiredWidth = mConfirmationTextView.getMeasuredWidth() - + confirmationLayoutParams.rightMargin - + confirmationLayoutParams.leftMargin - + mUndoView.getMeasuredWidth() - + undoLayoutParams.rightMargin; - // If the measured width isn't enough to accommodate both the undo button and the text in - // the same line, we'll need to adjust the view to be multi-line. Otherwise, we're done. - if (requiredWidth > measuredWidth) { - mIsMultiline = true; - - // Update height requirement to the text height and the button's height (along with - // additional spacing for the top of the text). - int updatedHeight = mMultilineTopMargin - + mConfirmationTextView.getMeasuredHeight() - + mUndoView.getMeasuredHeight() - + undoLayoutParams.topMargin - + undoLayoutParams.bottomMargin; - - setMeasuredDimension(measuredWidth, updatedHeight); - } else { - mIsMultiline = false; - } - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - // If the text view and undo view don't fit on the same line, we'll need to manually lay - // out the content. - if (mIsMultiline) { - // Re-align parent right/bottom values. Left and top are considered to be 0. - int parentBottom = getMeasuredHeight(); - int parentRight = getMeasuredWidth(); - - LayoutParams confirmationLayoutParams = - (LayoutParams) mConfirmationTextView.getLayoutParams(); - LayoutParams undoLayoutParams = (LayoutParams) mUndoView.getLayoutParams(); - - // The confirmation text occupies the full width as computed earlier. Both side margins - // are equivalent, so we only need to grab the left one here. - mConfirmationTextView.layout( - confirmationLayoutParams.leftMargin, - mMultilineTopMargin, - confirmationLayoutParams.leftMargin + mConfirmationTextView.getMeasuredWidth(), - mMultilineTopMargin + mConfirmationTextView.getMeasuredHeight()); - - // The undo button is aligned bottom|end with the parent in the case of multiline text. - int undoViewLeft = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL - ? undoLayoutParams.rightMargin - : parentRight - mUndoView.getMeasuredWidth() - undoLayoutParams.rightMargin; - mUndoView.layout( - undoViewLeft, - parentBottom - mUndoView.getMeasuredHeight() - undoLayoutParams.bottomMargin, - undoViewLeft + mUndoView.getMeasuredWidth(), - parentBottom - undoLayoutParams.bottomMargin); - } else { - super.onLayout(changed, left, top, right, bottom); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index c29ec9eeaf56..cf9d43eeff73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -26,7 +26,6 @@ import android.graphics.Color; import android.graphics.drawable.Animatable2; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; -import android.hardware.biometrics.BiometricSourceType; import android.os.Trace; import android.provider.Settings; import android.text.TextUtils; @@ -34,22 +33,17 @@ import android.util.AttributeSet; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityNodeInfo; -import androidx.annotation.Nullable; - import com.android.internal.graphics.ColorUtils; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -61,8 +55,7 @@ import javax.inject.Named; * Manages the different states and animations of the unlock icon. */ public class LockIcon extends KeyguardAffordanceView implements - KeyguardStateController.Callback, NotificationWakeUpCoordinator.WakeUpListener, - ViewTreeObserver.OnPreDrawListener, OnHeadsUpChangedListener { + ViewTreeObserver.OnPreDrawListener { private static final int STATE_LOCKED = 0; private static final int STATE_LOCK_OPEN = 1; @@ -70,7 +63,6 @@ public class LockIcon extends KeyguardAffordanceView implements private static final int STATE_BIOMETRICS_ERROR = 3; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final AccessibilityController mAccessibilityController; - private final DockManager mDockManager; private final KeyguardStateController mKeyguardStateController; private final KeyguardBypassController mBypassController; private final NotificationWakeUpCoordinator mWakeUpCoordinator; @@ -130,43 +122,6 @@ public class LockIcon extends KeyguardAffordanceView implements update(); } }; - private final DockManager.DockEventListener mDockEventListener = - new DockManager.DockEventListener() { - @Override - public void onEvent(int event) { - boolean docked = event == DockManager.STATE_DOCKED - || event == DockManager.STATE_DOCKED_HIDE; - if (docked != mDocked) { - mDocked = docked; - update(); - } - } - }; - - private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onSimStateChanged(int subId, int slotId, int simState) { - mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); - update(); - } - - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - update(); - } - - @Override - public void onBiometricRunningStateChanged(boolean running, - BiometricSourceType biometricSourceType) { - update(); - } - - @Override - public void onStrongAuthStateChanged(int userId) { - update(); - } - }; @Inject public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, @@ -174,7 +129,6 @@ public class LockIcon extends KeyguardAffordanceView implements KeyguardBypassController bypassController, NotificationWakeUpCoordinator wakeUpCoordinator, KeyguardStateController keyguardStateController, - @Nullable DockManager dockManager, HeadsUpManagerPhone headsUpManager) { super(context, attrs); mContext = context; @@ -183,7 +137,6 @@ public class LockIcon extends KeyguardAffordanceView implements mBypassController = bypassController; mWakeUpCoordinator = wakeUpCoordinator; mKeyguardStateController = keyguardStateController; - mDockManager = dockManager; mHeadsUpManager = headsUpManager; } @@ -191,24 +144,14 @@ public class LockIcon extends KeyguardAffordanceView implements protected void onAttachedToWindow() { super.onAttachedToWindow(); mKeyguardStateController.addCallback(mKeyguardMonitorCallback); - mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); - mWakeUpCoordinator.addListener(this); mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); - if (mDockManager != null) { - mDockManager.addListener(mDockEventListener); - } update(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); mKeyguardStateController.removeCallback(mKeyguardMonitorCallback); - mWakeUpCoordinator.removeListener(this); - if (mDockManager != null) { - mDockManager.removeListener(mDockEventListener); - } } /** @@ -306,7 +249,7 @@ public class LockIcon extends KeyguardAffordanceView implements * Update the icon visibility * @return true if the visibility changed */ - private boolean updateIconVisibility() { + boolean updateIconVisibility() { boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked); boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance; @@ -424,16 +367,6 @@ public class LockIcon extends KeyguardAffordanceView implements return -1; } - @Override - public void onFullyHiddenChanged(boolean isFullyHidden) { - if (mBypassController.getBypassEnabled()) { - boolean changed = updateIconVisibility(); - if (changed) { - update(); - } - } - } - public void setBouncerShowingScrimmed(boolean bouncerShowing) { mBouncerShowingScrimmed = bouncerShowing; if (mBypassController.getBypassEnabled()) { @@ -454,6 +387,18 @@ public class LockIcon extends KeyguardAffordanceView implements updateDarkTint(); } + void setSimLocked(boolean simLocked) { + mSimLocked = simLocked; + } + + /** Set if the device is docked. */ + public void setDocked(boolean docked) { + if (mDocked != docked) { + mDocked = docked; + update(); + } + } + @Retention(RetentionPolicy.SOURCE) @IntDef({ERROR, UNLOCK, LOCK, SCANNING}) @interface LockAnimIndex {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 698a430c389e..2b1a8a472d9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -18,20 +18,29 @@ package com.android.systemui.statusbar.phone; import android.content.res.TypedArray; import android.graphics.Color; +import android.hardware.biometrics.BiometricSourceType; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.Nullable; + import com.android.internal.logging.nano.MetricsProto; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; +import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator.WakeUpListener; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import java.util.Optional; + import javax.inject.Inject; import javax.inject.Singleton; @@ -47,6 +56,9 @@ public class LockscreenLockIconController { private final KeyguardIndicationController mKeyguardIndicationController; private final StatusBarStateController mStatusBarStateController; private final ConfigurationController mConfigurationController; + private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; + private final KeyguardBypassController mKeyguardBypassController; + private final Optional<DockManager> mDockManager; private LockIcon mLockIcon; private View.OnAttachStateChangeListener mOnAttachStateChangeListener = @@ -55,6 +67,10 @@ public class LockscreenLockIconController { public void onViewAttachedToWindow(View v) { mStatusBarStateController.addCallback(mSBStateListener); mConfigurationController.addCallback(mConfigurationListener); + mNotificationWakeUpCoordinator.addListener(mWakeUpListener); + mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); + + mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener)); mConfigurationListener.onThemeChanged(); } @@ -63,6 +79,11 @@ public class LockscreenLockIconController { public void onViewDetachedFromWindow(View v) { mStatusBarStateController.removeCallback(mSBStateListener); mConfigurationController.removeCallback(mConfigurationListener); + mNotificationWakeUpCoordinator.removeListener(mWakeUpListener); + mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); + + + mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener)); } }; @@ -115,6 +136,47 @@ public class LockscreenLockIconController { } }; + private final WakeUpListener mWakeUpListener = new WakeUpListener() { + @Override + public void onFullyHiddenChanged(boolean isFullyHidden) { + if (mKeyguardBypassController.getBypassEnabled()) { + boolean changed = mLockIcon.updateIconVisibility(); + if (changed) { + mLockIcon.update(); + } + } + } + }; + + private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onSimStateChanged(int subId, int slotId, int simState) { + mLockIcon.setSimLocked(mKeyguardUpdateMonitor.isSimPinSecure()); + mLockIcon.update(); + } + + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + mLockIcon.update(); + } + + @Override + public void onBiometricRunningStateChanged(boolean running, + BiometricSourceType biometricSourceType) { + mLockIcon.update(); + } + + @Override + public void onStrongAuthStateChanged(int userId) { + mLockIcon.update(); + } + }; + + private final DockManager.DockEventListener mDockEventListener = + event -> mLockIcon.setDocked(event == DockManager.STATE_DOCKED + || event == DockManager.STATE_DOCKED_HIDE); + @Inject public LockscreenLockIconController(LockscreenGestureLogger lockscreenGestureLogger, KeyguardUpdateMonitor keyguardUpdateMonitor, @@ -123,7 +185,10 @@ public class LockscreenLockIconController { AccessibilityController accessibilityController, KeyguardIndicationController keyguardIndicationController, StatusBarStateController statusBarStateController, - ConfigurationController configurationController) { + ConfigurationController configurationController, + NotificationWakeUpCoordinator notificationWakeUpCoordinator, + KeyguardBypassController keyguardBypassController, + @Nullable DockManager dockManager) { mLockscreenGestureLogger = lockscreenGestureLogger; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; @@ -132,6 +197,9 @@ public class LockscreenLockIconController { mKeyguardIndicationController = keyguardIndicationController; mStatusBarStateController = statusBarStateController; mConfigurationController = configurationController; + mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; + mKeyguardBypassController = keyguardBypassController; + mDockManager = dockManager == null ? Optional.empty() : Optional.of(dockManager); mKeyguardIndicationController.setLockIconController(this); } 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 d2d76c7e853c..b84208c9ac35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -23,7 +23,6 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE; import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT; import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; -import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM; import android.content.BroadcastReceiver; import android.content.Context; @@ -606,7 +605,7 @@ public class NetworkControllerImpl extends BroadcastReceiver } private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) { - if (subscriptions.size() == MODEM_COUNT_DUAL_MODEM) { + if (subscriptions.size() == 2) { SubscriptionInfo info1 = subscriptions.get(0); SubscriptionInfo info2 = subscriptions.get(1); if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java index 6c09a46833a2..cd461101e3c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.verify; import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.view.IWindowContainer; import android.view.SurfaceControl; import androidx.test.filters.SmallTest; @@ -51,7 +50,7 @@ public class PipAnimationControllerTest extends SysuiTestCase { private PipAnimationController mPipAnimationController; @Mock - private IWindowContainer mWindowContainer; + private SurfaceControl mLeash; @Mock private PipAnimationController.PipAnimationCallback mPipAnimationCallback; @@ -65,8 +64,7 @@ public class PipAnimationControllerTest extends SysuiTestCase { @Test public void getAnimator_withAlpha_returnFloatAnimator() { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mWindowContainer, true /* scheduleFinishPip */, - new Rect(), 0f, 1f); + .getAnimator(mLeash, true /* scheduleFinishPip */, new Rect(), 0f, 1f); assertEquals("Expect ANIM_TYPE_ALPHA animation", animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA); @@ -75,8 +73,7 @@ public class PipAnimationControllerTest extends SysuiTestCase { @Test public void getAnimator_withBounds_returnBoundsAnimator() { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mWindowContainer, true /* scheduleFinishPip */, - new Rect(), new Rect()); + .getAnimator(mLeash, true /* scheduleFinishPip */, new Rect(), new Rect()); assertEquals("Expect ANIM_TYPE_BOUNDS animation", animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS); @@ -88,14 +85,12 @@ public class PipAnimationControllerTest extends SysuiTestCase { final Rect endValue1 = new Rect(100, 100, 200, 200); final Rect endValue2 = new Rect(200, 200, 300, 300); final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController - .getAnimator(mWindowContainer, true /* scheduleFinishPip */, - startValue, endValue1); + .getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue1); oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); oldAnimator.start(); final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController - .getAnimator(mWindowContainer, true /* scheduleFinishPip */, - startValue, endValue2); + .getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue2); assertEquals("getAnimator with same type returns same animator", oldAnimator, newAnimator); @@ -106,13 +101,11 @@ public class PipAnimationControllerTest extends SysuiTestCase { @Test public void getAnimator_scheduleFinishPip() { PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mWindowContainer, true /* scheduleFinishPip */, - new Rect(), 0f, 1f); + .getAnimator(mLeash, true /* scheduleFinishPip */, new Rect(), 0f, 1f); assertTrue("scheduleFinishPip is true", animator.shouldScheduleFinishPip()); animator = mPipAnimationController - .getAnimator(mWindowContainer, false /* scheduleFinishPip */, - new Rect(), 0f, 1f); + .getAnimator(mLeash, false /* scheduleFinishPip */, new Rect(), 0f, 1f); assertFalse("scheduleFinishPip is false", animator.shouldScheduleFinishPip()); } @@ -122,8 +115,7 @@ public class PipAnimationControllerTest extends SysuiTestCase { final Rect endValue1 = new Rect(100, 100, 200, 200); final Rect endValue2 = new Rect(200, 200, 300, 300); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mWindowContainer, true /* scheduleFinishPip */, - startValue, endValue1); + .getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue1); animator.updateEndValue(endValue2); @@ -135,24 +127,23 @@ public class PipAnimationControllerTest extends SysuiTestCase { final Rect startValue = new Rect(0, 0, 100, 100); final Rect endValue = new Rect(100, 100, 200, 200); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mWindowContainer, true /* scheduleFinishPip */, - startValue, endValue); + .getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue); animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); animator.setPipAnimationCallback(mPipAnimationCallback); // onAnimationStart triggers onPipAnimationStart animator.onAnimationStart(animator); - verify(mPipAnimationCallback).onPipAnimationStart(mWindowContainer, animator); + verify(mPipAnimationCallback).onPipAnimationStart(animator); // onAnimationCancel triggers onPipAnimationCancel animator.onAnimationCancel(animator); - verify(mPipAnimationCallback).onPipAnimationCancel(mWindowContainer, animator); + verify(mPipAnimationCallback).onPipAnimationCancel(animator); // onAnimationEnd triggers onPipAnimationEnd animator.onAnimationEnd(animator); - verify(mPipAnimationCallback).onPipAnimationEnd(eq(mWindowContainer), - any(SurfaceControl.Transaction.class), eq(animator)); + verify(mPipAnimationCallback).onPipAnimationEnd(any(SurfaceControl.Transaction.class), + eq(animator)); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 54c0bde13408..e9dca699917c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -316,74 +316,9 @@ public class NotificationGutsManagerTest extends SysuiTestCase { } @Test - public void testInitializeNotificationInfoView_showBlockingHelper() throws Exception { - NotificationInfo notificationInfoView = mock(NotificationInfo.class); - ExpandableNotificationRow row = spy(mHelper.createRow()); - row.setBlockingHelperShowing(true); - modifyRanking(row.getEntry()) - .setUserSentiment(USER_SENTIMENT_NEGATIVE) - .build(); - when(row.getIsNonblockable()).thenReturn(false); - StatusBarNotification statusBarNotification = row.getEntry().getSbn(); - NotificationEntry entry = row.getEntry(); - - mGutsManager.initializeNotificationInfo(row, notificationInfoView); - - verify(notificationInfoView).bindNotification( - any(PackageManager.class), - any(INotificationManager.class), - eq(mVisualStabilityManager), - eq(statusBarNotification.getPackageName()), - any(NotificationChannel.class), - anySet(), - eq(entry), - any(NotificationInfo.CheckSaveListener.class), - any(NotificationInfo.OnSettingsClickListener.class), - any(NotificationInfo.OnAppSettingsClickListener.class), - eq(false), - eq(false), - eq(true) /* isForBlockingHelper */, - eq(0), - eq(false) /* wasShownHighPriority */); - } - - @Test - public void testInitializeNotificationInfoView_dontShowBlockingHelper() throws Exception { - NotificationInfo notificationInfoView = mock(NotificationInfo.class); - ExpandableNotificationRow row = spy(mHelper.createRow()); - row.setBlockingHelperShowing(false); - modifyRanking(row.getEntry()) - .setUserSentiment(USER_SENTIMENT_NEGATIVE) - .build(); - when(row.getIsNonblockable()).thenReturn(false); - StatusBarNotification statusBarNotification = row.getEntry().getSbn(); - NotificationEntry entry = row.getEntry(); - - mGutsManager.initializeNotificationInfo(row, notificationInfoView); - - verify(notificationInfoView).bindNotification( - any(PackageManager.class), - any(INotificationManager.class), - eq(mVisualStabilityManager), - eq(statusBarNotification.getPackageName()), - any(NotificationChannel.class), - anySet(), - eq(entry), - any(NotificationInfo.CheckSaveListener.class), - any(NotificationInfo.OnSettingsClickListener.class), - any(NotificationInfo.OnAppSettingsClickListener.class), - eq(false), - eq(false), - eq(false) /* isForBlockingHelper */, - eq(0), - eq(false) /* wasShownHighPriority */); - } - - @Test public void testInitializeNotificationInfoView_highPriority() throws Exception { NotificationInfo notificationInfoView = mock(NotificationInfo.class); ExpandableNotificationRow row = spy(mHelper.createRow()); - row.setBlockingHelperShowing(true); final NotificationEntry entry = row.getEntry(); modifyRanking(entry) .setUserSentiment(USER_SENTIMENT_NEGATIVE) @@ -403,13 +338,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { any(NotificationChannel.class), anySet(), eq(entry), - any(NotificationInfo.CheckSaveListener.class), any(NotificationInfo.OnSettingsClickListener.class), any(NotificationInfo.OnAppSettingsClickListener.class), eq(false), eq(false), - eq(true) /* isForBlockingHelper */, - eq(IMPORTANCE_HIGH), eq(true) /* wasShownHighPriority */); } @@ -437,13 +369,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { any(NotificationChannel.class), anySet(), eq(entry), - any(NotificationInfo.CheckSaveListener.class), any(NotificationInfo.OnSettingsClickListener.class), any(NotificationInfo.OnAppSettingsClickListener.class), eq(true), eq(false), - eq(false) /* isForBlockingHelper */, - eq(0), eq(false) /* wasShownHighPriority */); } @@ -469,13 +398,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { any(NotificationChannel.class), anySet(), eq(entry), - any(NotificationInfo.CheckSaveListener.class), any(NotificationInfo.OnSettingsClickListener.class), any(NotificationInfo.OnAppSettingsClickListener.class), eq(false), eq(false), - eq(true) /* isForBlockingHelper */, - eq(0), eq(false) /* wasShownHighPriority */); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index c62487a830fc..98ef691ee28c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -119,15 +119,10 @@ public class NotificationInfoTest extends SysuiTestCase { @Mock private PackageManager mMockPackageManager; @Mock - private NotificationBlockingHelperManager mBlockingHelperManager; - @Mock private VisualStabilityManager mVisualStabilityManager; @Before public void setUp() throws Exception { - mDependency.injectTestDependency( - NotificationBlockingHelperManager.class, - mBlockingHelperManager); mTestableLooper = TestableLooper.get(this); mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); @@ -183,13 +178,6 @@ public class NotificationInfoTest extends SysuiTestCase { NOTIFICATION_NEW_INTERRUPTION_MODEL, 0); } - // TODO: if tests are taking too long replace this with something that makes the animation - // finish instantly. - private void waitForUndoButton() { - PollingCheck.waitFor(1000, - () -> VISIBLE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility()); - } - @Test public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); @@ -203,12 +191,10 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); - final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); + final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); } @@ -228,12 +214,10 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); - final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); + final ImageView iconView = mNotificationInfo.findViewById(R.id.pkg_icon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -249,14 +233,12 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(GONE, nameView.getVisibility()); - final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider); + final TextView dividerView = mNotificationInfo.findViewById(R.id.group_divider); assertEquals(GONE, dividerView.getVisibility()); } @@ -281,16 +263,12 @@ public class NotificationInfoTest extends SysuiTestCase { entry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(VISIBLE, nameView.getVisibility()); assertTrue(nameView.getText().toString().contains("Proxied")); - final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider); - assertEquals(VISIBLE, dividerView.getVisibility()); } @Test @@ -305,13 +283,13 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(GONE, groupNameView.getVisibility()); + final TextView dividerView = mNotificationInfo.findViewById(R.id.group_divider); + assertEquals(GONE, dividerView.getVisibility()); } @Test @@ -332,14 +310,14 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); + final TextView dividerView = mNotificationInfo.findViewById(R.id.group_divider); + assertEquals(View.VISIBLE, dividerView.getVisibility()); } @Test @@ -354,10 +332,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); @@ -375,10 +351,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, textView.getVisibility()); @@ -387,7 +361,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_DefaultChannelUsesChannelNameIfMoreChannelsExist() throws Exception { - // Package has one channel by default. + // Package has more than one channel by default. when(mMockINotificationManager.getNumNotificationChannelsForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10); mNotificationInfo.bindNotification( @@ -400,10 +374,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); @@ -421,42 +393,14 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, true, - IMPORTANCE_DEFAULT, true); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @Test - public void testBindNotification_BlockLink_BlockingHelper() throws Exception { - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mEntry, - null, - mock(NotificationInfo.OnSettingsClickListener.class), - null, - true, - false, - true /* isBlockingHelper */, - IMPORTANCE_DEFAULT, - true); - final View block = - mNotificationInfo.findViewById(R.id.blocking_helper_turn_off_notifications); - final View interruptivenessSettings = mNotificationInfo.findViewById( - R.id.inline_controls); - assertEquals(VISIBLE, block.getVisibility()); - assertEquals(GONE, interruptivenessSettings.getVisibility()); - } - - @Test public void testBindNotification_SetsOnClickListenerForSettings() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification( @@ -467,7 +411,6 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel, mNotificationChannelSet, mEntry, - null, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); @@ -475,7 +418,6 @@ public class NotificationInfoTest extends SysuiTestCase { null, true, false, - IMPORTANCE_DEFAULT, true); final View settingsButton = mNotificationInfo.findViewById(R.id.info); @@ -496,10 +438,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); @@ -516,14 +456,12 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel, mNotificationChannelSet, mEntry, - null, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); }, null, false, false, - IMPORTANCE_DEFAULT, true); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); @@ -541,10 +479,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); mNotificationInfo.bindNotification( mMockPackageManager, @@ -554,89 +490,16 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel, mNotificationChannelSet, mEntry, - null, (View v, NotificationChannel c, int appUid) -> { }, null, true, false, - IMPORTANCE_DEFAULT, true); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @Test - public void testBindNotificationLogging_notBlockingHelper() throws Exception { - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mEntry, - null, - null, - null, - true, - false, - IMPORTANCE_DEFAULT, - true); - verify(mMetricsLogger).write(argThat(logMaker -> - logMaker.getCategory() == MetricsEvent.ACTION_NOTE_CONTROLS - && logMaker.getType() == MetricsEvent.TYPE_OPEN - && logMaker.getSubtype() == MetricsEvent.BLOCKING_HELPER_UNKNOWN - )); - } - - @Test - public void testBindNotificationLogging_BlockingHelper() throws Exception { - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mEntry, - null, - null, - null, - false, - true, - true, - IMPORTANCE_DEFAULT, - true); - verify(mMetricsLogger).write(argThat(logMaker -> - logMaker.getCategory() == MetricsEvent.ACTION_NOTE_CONTROLS - && logMaker.getType() == MetricsEvent.TYPE_OPEN - && logMaker.getSubtype() == MetricsEvent.BLOCKING_HELPER_DISPLAY - )); - } - - @Test - public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception { - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mEntry, - null, - null, - null, - false, - true, - true, - IMPORTANCE_DEFAULT, - true); - mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); - verify(mMetricsLogger).count(eq("HowCanNotifsBeRealIfAppsArent"), eq(1)); - } - - @Test public void testOnClickListenerPassesNullChannelForBundle() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification( @@ -646,7 +509,6 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mEntry, - null, (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); @@ -654,7 +516,6 @@ public class NotificationInfoTest extends SysuiTestCase { null, true, true, - IMPORTANCE_DEFAULT, true); mNotificationInfo.findViewById(R.id.info).performClick(); @@ -676,10 +537,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); final TextView channelNameView = mNotificationInfo.findViewById(R.id.channel_name); @@ -699,10 +558,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); assertEquals(GONE, mNotificationInfo.findViewById( R.id.interruptiveness_settings).getVisibility()); @@ -722,10 +579,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, true, - IMPORTANCE_DEFAULT, true); final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text); assertEquals(View.VISIBLE, view.getVisibility()); @@ -747,10 +602,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); assertTrue(mNotificationInfo.findViewById(R.id.alert).isSelected()); } @@ -767,10 +620,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, false); assertTrue(mNotificationInfo.findViewById(R.id.silence).isSelected()); } @@ -787,10 +638,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -810,10 +659,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_LOW, false); mNotificationInfo.findViewById(R.id.alert).performClick(); @@ -836,10 +683,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); mNotificationInfo.findViewById(R.id.silence).performClick(); @@ -862,10 +707,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); mNotificationInfo.handleCloseControls(true, false); @@ -889,10 +732,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_UNSPECIFIED, true); mNotificationInfo.handleCloseControls(true, false); @@ -904,225 +745,6 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test - public void testCloseControls_nonNullCheckSaveListenerDoesntDelayKeepShowing_BlockingHelper() - throws Exception { - NotificationInfo.CheckSaveListener listener = - mock(NotificationInfo.CheckSaveListener.class); - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel /* notificationChannel */, - createMultipleChannelSet(10) /* numUniqueChannelsInRow */, - mEntry, - listener /* checkSaveListener */, - null /* onSettingsClick */, - null /* onAppSettingsClick */, - true /* provisioned */, - false /* isNonblockable */, - true /* isForBlockingHelper */, - IMPORTANCE_DEFAULT, - true); - - NotificationGuts guts = spy(new NotificationGuts(mContext, null)); - when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); - doNothing().when(guts).animateClose(anyInt(), anyInt(), anyBoolean()); - doNothing().when(guts).setExposed(anyBoolean(), anyBoolean()); - guts.setGutsContent(mNotificationInfo); - mNotificationInfo.setGutsParent(guts); - - mNotificationInfo.findViewById(R.id.keep_showing).performClick(); - - verify(mBlockingHelperManager).dismissCurrentBlockingHelper(); - mTestableLooper.processAllMessages(); - verify(mMockINotificationManager, times(1)) - .setNotificationsEnabledWithImportanceLockForPackage( - anyString(), eq(TEST_UID), eq(true)); - } - - @Test - public void testCloseControls_nonNullCheckSaveListenerDoesntDelayDismiss_BlockingHelper() - throws Exception { - NotificationInfo.CheckSaveListener listener = - mock(NotificationInfo.CheckSaveListener.class); - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel /* notificationChannel */, - createMultipleChannelSet(10) /* numUniqueChannelsInRow */, - mEntry, - listener /* checkSaveListener */, - null /* onSettingsClick */, - null /* onAppSettingsClick */, - false /* isNonblockable */, - true /* isForBlockingHelper */, - true, IMPORTANCE_DEFAULT, - true); - - mNotificationInfo.handleCloseControls(true /* save */, false /* force */); - - mTestableLooper.processAllMessages(); - verify(listener, times(0)).checkSave(any(Runnable.class), eq(mSbn)); - } - - @Test - public void testCloseControls_checkSaveListenerDelaysStopNotifications_BlockingHelper() - throws Exception { - NotificationInfo.CheckSaveListener listener = - mock(NotificationInfo.CheckSaveListener.class); - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel /* notificationChannel */, - createMultipleChannelSet(10) /* numUniqueChannelsInRow */, - mEntry, - listener /* checkSaveListener */, - null /* onSettingsClick */, - null /* onAppSettingsClick */, - true /* provisioned */, - false /* isNonblockable */, - true /* isForBlockingHelper */, - IMPORTANCE_DEFAULT, - true); - - mNotificationInfo.findViewById(R.id.deliver_silently).performClick(); - mTestableLooper.processAllMessages(); - verify(listener).checkSave(any(Runnable.class), eq(mSbn)); - } - - @Test - public void testCloseControls_blockingHelperDismissedIfShown() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet /* numChannels */, - mEntry, - null /* checkSaveListener */, - null /* onSettingsClick */, - null /* onAppSettingsClick */, - false /* isNonblockable */, - true /* isForBlockingHelper */, - true, - IMPORTANCE_DEFAULT, - true); - NotificationGuts guts = mock(NotificationGuts.class); - doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean()); - mNotificationInfo.setGutsParent(guts); - - mNotificationInfo.closeControls(mNotificationInfo, true); - - verify(mBlockingHelperManager).dismissCurrentBlockingHelper(); - } - - @Test - public void testSilentlyChangedCallsUpdateNotificationChannel_blockingHelper() - throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet /* numChannels */, - mEntry, - null /* checkSaveListener */, - null /* onSettingsClick */, - null /* onAppSettingsClick */, - true /*provisioned */, - false /* isNonblockable */, - true /* isForBlockingHelper */, - IMPORTANCE_DEFAULT, - true); - - mNotificationInfo.findViewById(R.id.deliver_silently).performClick(); - waitForUndoButton(); - mNotificationInfo.handleCloseControls(true, false); - - mTestableLooper.processAllMessages(); - ArgumentCaptor<NotificationChannel> updated = - ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( - anyString(), eq(TEST_UID), updated.capture()); - assertTrue((updated.getValue().getUserLockedFields() - & USER_LOCKED_IMPORTANCE) != 0); - assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance()); - } - - @Test - public void testKeepUpdatesNotificationChannel_blockingHelper() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mEntry, - null, - null, - null, - true, - true, - IMPORTANCE_LOW, - false); - - mNotificationInfo.findViewById(R.id.keep_showing).performClick(); - mNotificationInfo.handleCloseControls(true, false); - - mTestableLooper.processAllMessages(); - ArgumentCaptor<NotificationChannel> updated = - ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( - anyString(), eq(TEST_UID), updated.capture()); - assertTrue(0 != (mNotificationChannel.getUserLockedFields() & USER_LOCKED_IMPORTANCE)); - assertEquals(IMPORTANCE_LOW, mNotificationChannel.getImportance()); - } - - @Test - public void testNoActionsUpdatesNotificationChannel_blockingHelper() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mEntry, - null, - null, - null, - true, - true, - IMPORTANCE_DEFAULT, - true); - - mNotificationInfo.handleCloseControls(true, false); - - mTestableLooper.processAllMessages(); - ArgumentCaptor<NotificationChannel> updated = - ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( - anyString(), eq(TEST_UID), updated.capture()); - assertTrue(0 != (mNotificationChannel.getUserLockedFields() & USER_LOCKED_IMPORTANCE)); - assertEquals(IMPORTANCE_DEFAULT, mNotificationChannel.getImportance()); - } - - @Test public void testSilenceCallsUpdateNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification( @@ -1135,10 +757,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); mNotificationInfo.findViewById(R.id.silence).performClick(); @@ -1168,10 +788,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_LOW, false); mNotificationInfo.findViewById(R.id.alert).performClick(); @@ -1202,10 +820,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_UNSPECIFIED, true); mNotificationInfo.findViewById(R.id.silence).performClick(); @@ -1236,10 +852,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_MIN, false); assertEquals(mContext.getString(R.string.inline_done_button), @@ -1273,10 +887,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_MIN, false); assertEquals(mContext.getString(R.string.inline_done_button), @@ -1309,10 +921,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); mNotificationInfo.findViewById(R.id.silence).performClick(); @@ -1336,10 +946,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_LOW, false); assertEquals(mContext.getString(R.string.inline_done_button), @@ -1366,10 +974,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_LOW, false); mNotificationInfo.findViewById(R.id.alert).performClick(); @@ -1399,10 +1005,8 @@ public class NotificationInfoTest extends SysuiTestCase { mEntry, null, null, - null, true, false, - IMPORTANCE_LOW, false); mNotificationInfo.findViewById(R.id.alert).performClick(); @@ -1425,14 +1029,10 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel, mNotificationChannelSet, mEntry, - (Runnable saveImportance, StatusBarNotification sbn) -> { - saveImportance.run(); - }, null, null, true, false, - IMPORTANCE_LOW, false ); @@ -1460,14 +1060,10 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel, mNotificationChannelSet, mEntry, - (Runnable saveImportance, StatusBarNotification sbn) -> { - saveImportance.run(); - }, null, null, true, false, - IMPORTANCE_LOW, false ); @@ -1488,14 +1084,10 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel, mNotificationChannelSet, mEntry, - (Runnable saveImportance, StatusBarNotification sbn) -> { - saveImportance.run(); - }, null, null, true, false, - IMPORTANCE_LOW, false ); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java index 05f10e376f61..487885ac244e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java @@ -29,8 +29,10 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -63,6 +65,13 @@ public class LockscreenIconControllerTest extends SysuiTestCase { private StatusBarStateController mStatusBarStateController; @Mock private ConfigurationController mConfigurationController; + @Mock + private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; + @Mock + private KeyguardBypassController mKeyguardBypassController; + @Mock + private DockManager mDockManager; + @Before public void setUp() { @@ -71,7 +80,8 @@ public class LockscreenIconControllerTest extends SysuiTestCase { mLockIconController = new LockscreenLockIconController( mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils, mShadeController, mAccessibilityController, mKeyguardIndicationController, - mStatusBarStateController, mConfigurationController); + mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator, + mKeyguardBypassController, mDockManager); mLockIconController.attach(mLockIcon); } diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index 41207c97492a..318a03074492 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -21,6 +21,7 @@ import android.content.pm.PackageManager; import android.gsi.AvbPublicKey; import android.gsi.GsiProgress; import android.gsi.IGsiService; +import android.gsi.IGsiServiceCallback; import android.gsi.IGsid; import android.os.Environment; import android.os.IBinder; @@ -115,6 +116,20 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements } } + class GsiServiceCallback extends IGsiServiceCallback.Stub { + // 0 for success + private int mResult = -1; + + public synchronized void onResult(int result) { + mResult = result; + notify(); + } + + public int getResult() { + return mResult; + } + } + @Override public boolean startInstallation(String dsuSlot) throws RemoteException { IGsiService service = getGsiService(); @@ -186,7 +201,9 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements @Override public boolean isInstalled() throws RemoteException { - return getGsiService().isGsiInstalled(); + boolean installed = SystemProperties.getBoolean("gsid.image_installed", false); + Slog.i(TAG, "isInstalled(): " + installed); + return installed; } @Override @@ -196,16 +213,37 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements @Override public boolean remove() throws RemoteException { - IGsiService gsiService = getGsiService(); - String install_dir = gsiService.getInstalledGsiImageDir(); - return getGsiService().removeGsi(); + try { + GsiServiceCallback callback = new GsiServiceCallback(); + synchronized (callback) { + getGsiService().removeGsiAsync(callback); + callback.wait(GSID_ROUGH_TIMEOUT_MS); + } + return callback.getResult() == 0; + } catch (InterruptedException e) { + Slog.e(TAG, "remove() was interrupted"); + return false; + } } @Override public boolean setEnable(boolean enable, boolean oneShot) throws RemoteException { IGsiService gsiService = getGsiService(); if (enable) { - return gsiService.enableGsi(oneShot, mDsuSlot) == 0; + try { + if (mDsuSlot == null) { + mDsuSlot = gsiService.getActiveDsuSlot(); + } + GsiServiceCallback callback = new GsiServiceCallback(); + synchronized (callback) { + gsiService.enableGsiAsync(oneShot, mDsuSlot, callback); + callback.wait(GSID_ROUGH_TIMEOUT_MS); + } + return callback.getResult() == 0; + } catch (InterruptedException e) { + Slog.e(TAG, "setEnable() was interrupted"); + return false; + } } else { return gsiService.disableGsi(); } diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 88b517c597c4..7c833faee1bd 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -467,9 +467,9 @@ final class UiModeManagerService extends SystemService { mNightMode = Secure.getIntForUser(context.getContentResolver(), Secure.UI_NIGHT_MODE, defaultNightMode, userId); mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(), - Secure.UI_NIGHT_MODE_OVERRIDE_ON, defaultNightMode, userId) != 0; + Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0; mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(), - Secure.UI_NIGHT_MODE_OVERRIDE_OFF, defaultNightMode, userId) != 0; + Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0; mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay( Secure.getLongForUser(context.getContentResolver(), Secure.DARK_THEME_CUSTOM_START_TIME, @@ -1045,7 +1045,6 @@ final class UiModeManagerService extends SystemService { final TwilightState lastState = mTwilightManager.getLastTwilightState(); activateNightMode = lastState == null ? mComputedNightMode : lastState.isNight(); } - updateComputedNightModeLocked(activateNightMode); } else { if (mTwilightManager != null) { @@ -1375,6 +1374,9 @@ final class UiModeManagerService extends SystemService { private void updateComputedNightModeLocked(boolean activate) { mComputedNightMode = activate; + if (mNightMode == MODE_NIGHT_YES || mNightMode == UiModeManager.MODE_NIGHT_NO) { + return; + } if (mOverrideNightModeOn && !mComputedNightMode) { mComputedNightMode = true; return; diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 93b16f762fd9..056156745966 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -110,6 +110,7 @@ public class Watchdog extends Thread { "android.hardware.audio@5.0::IDevicesFactory", "android.hardware.audio@6.0::IDevicesFactory", "android.hardware.biometrics.face@1.0::IBiometricsFace", + "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint", "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.gnss@1.0::IGnss", diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 8fbe923cedc6..6b917bc2249d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -19,16 +19,12 @@ package com.android.server.am; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK; import android.app.ActivityThread; -import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; -import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; import android.os.Build; import android.os.Handler; -import android.os.UserHandle; -import android.os.UserManager; import android.provider.DeviceConfig; import android.provider.DeviceConfig.OnPropertiesChangedListener; import android.provider.DeviceConfig.Properties; @@ -37,7 +33,6 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.KeyValueListParser; import android.util.Slog; -import android.util.SparseArray; import java.io.PrintWriter; import java.util.Arrays; @@ -294,12 +289,6 @@ final class ActivityManagerConstants extends ContentObserver { // started, the restriction is on while-in-use permissions.) volatile boolean mFlagBackgroundFgsStartRestrictionEnabled = true; - /** - * UserId to Assistant ComponentName mapping. - * Per user Assistant ComponentName is from {@link android.provider.Settings.Secure#ASSISTANT} - */ - SparseArray<ComponentName> mAssistants = new SparseArray<>(); - private final ActivityManagerService mService; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -375,8 +364,6 @@ final class ActivityManagerConstants extends ContentObserver { Settings.Global.getUriFor( Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED); - private static final Uri ASSISTANT_URI = Settings.Secure.getUriFor(Settings.Secure.ASSISTANT); - private static final Uri ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI = Settings.Global.getUriFor(Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS); @@ -443,8 +430,6 @@ final class ActivityManagerConstants extends ContentObserver { mResolver.registerContentObserver(ACTIVITY_STARTS_LOGGING_ENABLED_URI, false, this); mResolver.registerContentObserver(FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED_URI, false, this); - mResolver.registerContentObserver(ASSISTANT_URI, false, this, - UserHandle.USER_ALL); if (mSystemServerAutomaticHeapDumpEnabled) { mResolver.registerContentObserver(ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI, false, this); @@ -460,7 +445,6 @@ final class ActivityManagerConstants extends ContentObserver { // The following read from Settings. updateActivityStartsLoggingEnabled(); updateForegroundServiceStartsLoggingEnabled(); - updateAssistant(); } private void loadDeviceConfigConstants() { @@ -492,8 +476,6 @@ final class ActivityManagerConstants extends ContentObserver { updateForegroundServiceStartsLoggingEnabled(); } else if (ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI.equals(uri)) { updateEnableAutomaticSystemServerHeapDumps(); - } else if (ASSISTANT_URI.equals(uri)) { - updateAssistant(); } } @@ -590,32 +572,6 @@ final class ActivityManagerConstants extends ContentObserver { mFlagForegroundServiceStartsLoggingEnabled = Settings.Global.getInt(mResolver, Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED, 1) == 1; } - - private void updateAssistant() { - final List<UserInfo> users = - mService.mContext.getSystemService(UserManager.class).getUsers(); - SparseArray<ComponentName> componentNames = new SparseArray<>(); - for (int i = 0; i < users.size(); i++) { - final int userId = users.get(i).id; - final String str = Settings.Secure.getStringForUser(mResolver, - Settings.Secure.ASSISTANT, userId); - if (!TextUtils.isEmpty(str)) { - componentNames.put(userId, ComponentName.unflattenFromString(str)); - } - } - synchronized (mService) { - for (int i = 0; i < mAssistants.size(); i++) { - mService.mServices.mWhiteListAllowWhileInUsePermissionInFgs.remove( - mAssistants.valueAt(i).getPackageName()); - } - mAssistants = componentNames; - for (int i = 0; i < mAssistants.size(); i++) { - mService.mServices.mWhiteListAllowWhileInUsePermissionInFgs.add( - mAssistants.valueAt(i).getPackageName()); - } - } - } - private void updateBackgroundFgsStartsRestriction() { mFlagBackgroundFgsStartRestrictionEnabled = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index 8687f35d745e..7bdeb5969f1f 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -63,7 +63,7 @@ public final class CompatChange extends CompatibilityChangeInfo { private Map<String, Boolean> mPackageOverrides; public CompatChange(long changeId) { - this(changeId, null, -1, false, null); + this(changeId, null, -1, false, false, null); } /** @@ -74,8 +74,8 @@ public final class CompatChange extends CompatibilityChangeInfo { * @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set. */ public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk, - boolean disabled, String description) { - super(changeId, name, enableAfterTargetSdk, disabled, description); + boolean disabled, boolean loggingOnly, String description) { + super(changeId, name, enableAfterTargetSdk, disabled, loggingOnly, description); } /** @@ -83,7 +83,7 @@ public final class CompatChange extends CompatibilityChangeInfo { */ public CompatChange(Change change) { super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(), - change.getDisabled(), change.getDescription()); + change.getDisabled(), change.getLoggingOnly(), change.getDescription()); } void registerListener(ChangeListener listener) { @@ -105,6 +105,10 @@ public final class CompatChange extends CompatibilityChangeInfo { * @param enabled Whether or not to enable the change. */ void addPackageOverride(String pname, boolean enabled) { + if (getLoggingOnly()) { + throw new IllegalArgumentException( + "Can't add overrides for a logging only change " + toString()); + } if (mPackageOverrides == null) { mPackageOverrides = new HashMap<>(); } @@ -160,6 +164,9 @@ public final class CompatChange extends CompatibilityChangeInfo { if (getDisabled()) { sb.append("; disabled"); } + if (getLoggingOnly()) { + sb.append("; loggingOnly"); + } if (mPackageOverrides != null && mPackageOverrides.size() > 0) { sb.append("; packageOverrides=").append(mPackageOverrides); } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 441d9d9f380e..bfc2f82d586d 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -206,6 +206,19 @@ final class CompatConfig { } /** + * Returns whether the change is marked as logging only. + */ + boolean isLoggingOnly(long changeId) { + synchronized (mChanges) { + CompatChange c = mChanges.get(changeId); + if (c == null) { + return false; + } + return c.getLoggingOnly(); + } + } + + /** * Removes an override previously added via {@link #addOverride(long, String, boolean)}. This * restores the default behaviour for the given change and app, once any app processes have been * restarted. @@ -365,6 +378,7 @@ final class CompatConfig { change.getName(), change.getEnableAfterTargetSdk(), change.getDisabled(), + change.getLoggingOnly(), change.getDescription()); } return changeInfos; diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java index 4bf606e801f9..9e18c74265d7 100644 --- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -20,6 +20,7 @@ import static com.android.internal.compat.OverrideAllowedState.ALLOWED; import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK; import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH; +import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE; import static com.android.internal.compat.OverrideAllowedState.PACKAGE_DOES_NOT_EXIST; import android.content.Context; @@ -51,12 +52,13 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { @Override public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) { - boolean debuggableBuild = false; - boolean finalBuild = false; - int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId); + if (mCompatConfig.isLoggingOnly(changeId)) { + return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1); + } - debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild(); - finalBuild = mAndroidBuildClassifier.isFinalBuild(); + boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild(); + boolean finalBuild = mAndroidBuildClassifier.isFinalBuild(); + int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId); // Allow any override for userdebug or eng builds. if (debuggableBuild) { diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 74c1e63172fa..61b18eedfc6e 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -83,6 +83,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Objects; /** * {@hide} @@ -399,15 +400,22 @@ public final class ContentService extends IContentService.Stub { public void notifyChange(Uri[] uris, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, int userHandle, int targetSdkVersion, String callingPackage) { + final ObserverCollector collector = new ObserverCollector(); for (Uri uri : uris) { notifyChange(uri, observer, observerWantsSelfNotifications, flags, userHandle, - targetSdkVersion, callingPackage); + targetSdkVersion, callingPackage, collector); + } + final long token = clearCallingIdentity(); + try { + collector.dispatch(); + } finally { + Binder.restoreCallingIdentity(token); } } public void notifyChange(Uri uri, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, int userHandle, - int targetSdkVersion, String callingPackage) { + int targetSdkVersion, String callingPackage, ObserverCollector collector) { if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle + " from observer " + observer + ", flags " + Integer.toHexString(flags)); @@ -442,22 +450,9 @@ public final class ContentService extends IContentService.Stub { // process rather than the caller's process. We will restore this before returning. long identityToken = clearCallingIdentity(); try { - ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); synchronized (mRootNode) { mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, - flags, userHandle, calls); - } - final int numCalls = calls.size(); - for (int i = 0; i < numCalls; i++) { - // Immediately dispatch notifications to foreground apps that - // are important to the user; all other background observers are - // delayed to avoid stampeding - final ObserverCall oc = calls.get(i); - if (oc.mProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { - oc.run(); - } else { - BackgroundThread.getHandler().postDelayed(oc, BACKGROUND_OBSERVER_DELAY); - } + flags, userHandle, collector); } if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) { SyncManager syncManager = getSyncManager(); @@ -487,40 +482,84 @@ public final class ContentService extends IContentService.Stub { } } - public void notifyChange(Uri uri, IContentObserver observer, - boolean observerWantsSelfNotifications, boolean syncToNetwork, - String callingPackage) { - notifyChange(uri, observer, observerWantsSelfNotifications, - syncToNetwork ? ContentResolver.NOTIFY_SYNC_TO_NETWORK : 0, - UserHandle.getCallingUserId(), Build.VERSION_CODES.CUR_DEVELOPMENT, callingPackage); - } - - /** {@hide} */ + /** + * Collection of detected change notifications that should be delivered. + * <p> + * To help reduce Binder transaction overhead, this class clusters together + * multiple {@link Uri} where all other arguments are identical. + */ @VisibleForTesting - public static final class ObserverCall implements Runnable { - final IContentObserver mObserver; - final boolean mSelfChange; - final Uri mUri; - final int mUserId; - final int mProcState; - - ObserverCall(IContentObserver observer, boolean selfChange, Uri uri, int userId, - int procState) { - mObserver = observer; - mSelfChange = selfChange; - mUri = uri; - mUserId = userId; - mProcState = procState; + public static class ObserverCollector { + private final ArrayMap<Key, List<Uri>> collected = new ArrayMap<>(); + + private static class Key { + final IContentObserver observer; + final int uid; + final boolean selfChange; + final int flags; + final int userId; + + Key(IContentObserver observer, int uid, boolean selfChange, int flags, int userId) { + this.observer = observer; + this.uid = uid; + this.selfChange = selfChange; + this.flags = flags; + this.userId = userId; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Key)) { + return false; + } + final Key other = (Key) o; + return Objects.equals(observer, other.observer) + && (uid == other.uid) + && (selfChange == other.selfChange) + && (flags == other.flags) + && (userId == other.userId); + } + + @Override + public int hashCode() { + return Objects.hash(observer, uid, selfChange, flags, userId); + } } - @Override - public void run() { - try { - mObserver.onChange(mSelfChange, mUri, mUserId); - if (DEBUG) Slog.d(TAG, "Notified " + mObserver + " of update at " + mUri); - } catch (RemoteException ignored) { - // We already have a death observer that will clean up if the - // remote process dies + public void collect(IContentObserver observer, int uid, boolean selfChange, Uri uri, + int flags, int userId) { + final Key key = new Key(observer, uid, selfChange, flags, userId); + List<Uri> value = collected.get(key); + if (value == null) { + value = new ArrayList<>(); + collected.put(key, value); + } + value.add(uri); + } + + public void dispatch() { + for (int i = 0; i < collected.size(); i++) { + final Key key = collected.keyAt(i); + final List<Uri> value = collected.valueAt(i); + + final Runnable task = () -> { + try { + key.observer.onChangeEtc(key.selfChange, + value.toArray(new Uri[value.size()]), key.flags, key.userId); + } catch (RemoteException ignored) { + } + }; + + // Immediately dispatch notifications to foreground apps that + // are important to the user; all other background observers are + // delayed to avoid stampeding + final int procState = LocalServices.getService(ActivityManagerInternal.class) + .getUidProcessState(key.uid); + if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { + task.run(); + } else { + BackgroundThread.getHandler().postDelayed(task, BACKGROUND_OBSERVER_DELAY); + } } } } @@ -1455,10 +1494,6 @@ public final class ContentService extends IContentService.Stub { } } - public static final int INSERT_TYPE = 0; - public static final int UPDATE_TYPE = 1; - public static final int DELETE_TYPE = 2; - private String mName; private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>(); private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>(); @@ -1588,7 +1623,7 @@ public final class ContentService extends IContentService.Stub { private void collectMyObserversLocked(Uri uri, boolean leaf, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, - int targetUserHandle, ArrayList<ObserverCall> calls) { + int targetUserHandle, ObserverCollector collector) { int N = mObservers.size(); IBinder observerBinder = observer == null ? null : observer.asBinder(); for (int i = 0; i < N; i++) { @@ -1628,10 +1663,8 @@ public final class ContentService extends IContentService.Stub { if (DEBUG) Slog.d(TAG, "Reporting to " + entry.observer + ": leaf=" + leaf + " flags=" + Integer.toHexString(flags) + " desc=" + entry.notifyForDescendants); - final int procState = LocalServices.getService(ActivityManagerInternal.class) - .getUidProcessState(entry.uid); - calls.add(new ObserverCall(entry.observer, selfChange, uri, - targetUserHandle, procState)); + collector.collect(entry.observer, entry.uid, selfChange, uri, flags, + targetUserHandle); } } } @@ -1641,21 +1674,21 @@ public final class ContentService extends IContentService.Stub { */ public void collectObserversLocked(Uri uri, int index, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, - int targetUserHandle, ArrayList<ObserverCall> calls) { + int targetUserHandle, ObserverCollector collector) { String segment = null; int segmentCount = countUriSegments(uri); if (index >= segmentCount) { // This is the leaf node, notify all observers if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName); collectMyObserversLocked(uri, true, observer, observerWantsSelfNotifications, - flags, targetUserHandle, calls); + flags, targetUserHandle, collector); } else if (index < segmentCount){ segment = getUriSegment(uri, index); if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / " + segment); // Notify any observers at this level who are interested in descendants collectMyObserversLocked(uri, false, observer, observerWantsSelfNotifications, - flags, targetUserHandle, calls); + flags, targetUserHandle, collector); } int N = mChildren.size(); @@ -1664,7 +1697,7 @@ public final class ContentService extends IContentService.Stub { if (segment == null || node.mName.equals(segment)) { // We found the child, node.collectObserversLocked(uri, index + 1, observer, - observerWantsSelfNotifications, flags, targetUserHandle, calls); + observerWantsSelfNotifications, flags, targetUserHandle, collector); if (segment != null) { break; } diff --git a/services/core/java/com/android/server/integrity/TEST_MAPPING b/services/core/java/com/android/server/integrity/TEST_MAPPING index ca7f396e23e4..495920454fd7 100644 --- a/services/core/java/com/android/server/integrity/TEST_MAPPING +++ b/services/core/java/com/android/server/integrity/TEST_MAPPING @@ -7,6 +7,14 @@ "include-filter": "com.android.server.integrity." } ] + }, + { + "name": "GtsSecurityHostTestCases", + "options": [ + { + "include-filter": "com.google.android.security.gts.AppIntegrityManagerTest" + } + ] } ] } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 6faf67486ff3..c1c37603b9c3 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -1572,6 +1572,24 @@ public class LockSettingsService extends ILockSettings.Stub { "This operation requires secure lock screen feature"); } checkWritePermission(userId); + + // When changing credential for profiles with unified challenge, some callers + // will pass in empty credential while others will pass in the credential of + // the parent user. setLockCredentialInternal() handles the formal case (empty + // credential) correctly but not the latter. As a stopgap fix, convert the latter + // case to the formal. The long-term fix would be fixing LSS such that it should + // accept only the parent user credential on its public API interfaces, swap it + // with the profile's random credential at that API boundary (i.e. here) and make + // sure LSS internally does not special case profile with unififed challenge: b/80170828. + if (!savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) { + // Verify the parent credential again, to make sure we have a fresh enough + // auth token such that getDecryptedPasswordForTiedProfile() inside + // setLockCredentialInternal() can function correctly. + verifyCredential(savedCredential, /* challenge */ 0, + mUserManager.getProfileParent(userId).id); + savedCredential.zeroize(); + savedCredential = LockscreenCredential.createNone(); + } synchronized (mSeparateChallengeLock) { if (!setLockCredentialInternal(credential, savedCredential, userId, /* isLockTiedToParent= */ false)) { @@ -1627,6 +1645,7 @@ public class LockSettingsService extends ILockSettings.Stub { // get credential from keystore when managed profile has unified lock if (savedCredential.isNone()) { try { + //TODO: remove as part of b/80170828 savedCredential = getDecryptedPasswordForTiedProfile(userId); } catch (FileNotFoundException e) { Slog.i(TAG, "Child profile key not found"); @@ -2876,6 +2895,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) { // get credential from keystore when managed profile has unified lock try { + //TODO: remove as part of b/80170828 savedCredential = getDecryptedPasswordForTiedProfile(userId); } catch (FileNotFoundException e) { Slog.i(TAG, "Child profile key not found"); diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index f144405436f9..4cb1ed9a0f9e 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -111,7 +111,6 @@ abstract class MediaRoute2Provider { void onProviderStateChanged(@Nullable MediaRoute2Provider provider); void onSessionCreated(@NonNull MediaRoute2Provider provider, @Nullable RoutingSessionInfo sessionInfo, long requestId); - void onSessionCreationFailed(@NonNull MediaRoute2Provider provider, long requestId); void onSessionUpdated(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo); void onSessionReleased(@NonNull MediaRoute2Provider provider, diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index e64776cc6e35..36a7bd983a29 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -328,19 +328,6 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mCallback.onSessionCreated(this, sessionInfo, requestId); } - private void onSessionCreationFailed(Connection connection, long requestId) { - if (mActiveConnection != connection) { - return; - } - - if (requestId == MediaRoute2ProviderService.REQUEST_ID_NONE) { - Slog.w(TAG, "onSessionCreationFailed: Ignoring requestId REQUEST_ID_NONE"); - return; - } - - mCallback.onSessionCreationFailed(this, requestId); - } - private void onSessionUpdated(Connection connection, RoutingSessionInfo sessionInfo) { if (mActiveConnection != connection) { return; @@ -543,10 +530,6 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mHandler.post(() -> onSessionCreated(Connection.this, sessionInfo, requestId)); } - void postSessionCreationFailed(long requestId) { - mHandler.post(() -> onSessionCreationFailed(Connection.this, requestId)); - } - void postSessionUpdated(RoutingSessionInfo sessionInfo) { mHandler.post(() -> onSessionUpdated(Connection.this, sessionInfo)); } @@ -555,7 +538,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mHandler.post(() -> onSessionReleased(Connection.this, sessionInfo)); } - void postSessionReleased(long requestId, int reason) { + void postRequestFailed(long requestId, int reason) { mHandler.post(() -> onRequestFailed(Connection.this, requestId, reason)); } } @@ -589,14 +572,6 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } @Override - public void notifySessionCreationFailed(long requestId) { - Connection connection = mConnectionRef.get(); - if (connection != null) { - connection.postSessionCreationFailed(requestId); - } - } - - @Override public void notifySessionUpdated(RoutingSessionInfo sessionInfo) { Connection connection = mConnectionRef.get(); if (connection != null) { @@ -616,7 +591,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider public void notifyRequestFailed(long requestId, int reason) { Connection connection = mConnectionRef.get(); if (connection != null) { - connection.postSessionReleased(requestId, reason); + connection.postRequestFailed(requestId, reason); } } } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index e78a35cd88a4..16f2addcb839 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -608,7 +608,8 @@ class MediaRouter2ServiceImpl { routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::requestCreateSessionOnHandler, routerRecord.mUserRecord.mHandler, - routerRecord, route, uniqueRequestId, sessionHints)); + routerRecord, /* managerRecord= */ null, route, uniqueRequestId, + sessionHints)); } private void selectRouteWithRouter2Locked(@NonNull IMediaRouter2 router, @@ -788,7 +789,8 @@ class MediaRouter2ServiceImpl { routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::requestCreateSessionOnHandler, routerRecord.mUserRecord.mHandler, - routerRecord, route, uniqueRequestId, null /* sessionHints */)); + routerRecord, managerRecord, route, uniqueRequestId, + /* sessionHints= */ null)); } private void selectRouteWithManagerLocked(@NonNull IMediaRouter2Manager manager, @@ -927,7 +929,7 @@ class MediaRouter2ServiceImpl { return ((long) routerOrManagerId << 32) | originalRequestId; } - static int toRouterOrManagerId(long uniqueRequestId) { + static int toRequesterId(long uniqueRequestId) { return (int) (uniqueRequestId >> 32); } @@ -1107,11 +1109,6 @@ class MediaRouter2ServiceImpl { this, provider, sessionInfo, requestId)); } - @Override - public void onSessionCreationFailed(@NonNull MediaRoute2Provider provider, long requestId) { - sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionCreationFailedOnHandler, - this, provider, requestId)); - } @Override public void onSessionUpdated(@NonNull MediaRoute2Provider provider, @@ -1223,7 +1220,8 @@ class MediaRouter2ServiceImpl { } private void requestCreateSessionOnHandler(@NonNull RouterRecord routerRecord, - @NonNull MediaRoute2Info route, long requestId, @Nullable Bundle sessionHints) { + @Nullable ManagerRecord managerRecord, @NonNull MediaRoute2Info route, + long requestId, @Nullable Bundle sessionHints) { final MediaRoute2Provider provider = findProvider(route.getProviderId()); if (provider == null) { @@ -1235,7 +1233,7 @@ class MediaRouter2ServiceImpl { // TODO: Apply timeout for each request (How many seconds should we wait?) SessionCreationRequest request = - new SessionCreationRequest(routerRecord, route, requestId); + new SessionCreationRequest(routerRecord, managerRecord, route, requestId); mSessionCreationRequests.add(request); provider.requestCreateSession(routerRecord.mPackageName, route.getOriginalId(), @@ -1421,30 +1419,6 @@ class MediaRouter2ServiceImpl { mSessionToRouterMap.put(sessionInfo.getId(), routerRecord); } - private void onSessionCreationFailedOnHandler(@NonNull MediaRoute2Provider provider, - long requestId) { - SessionCreationRequest matchingRequest = null; - - for (SessionCreationRequest request : mSessionCreationRequests) { - if (request.mRequestId == requestId - && TextUtils.equals( - request.mRoute.getProviderId(), provider.getUniqueId())) { - matchingRequest = request; - break; - } - } - - if (matchingRequest == null) { - Slog.w(TAG, "Ignoring session creation failed result for unknown request. " - + "requestId=" + requestId); - return; - } - - mSessionCreationRequests.remove(matchingRequest); - notifySessionCreationFailedToRouter(matchingRequest.mRouterRecord, - toOriginalRequestId(requestId)); - } - private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo) { List<IMediaRouter2Manager> managers = getManagers(); @@ -1483,30 +1457,56 @@ class MediaRouter2ServiceImpl { private void onRequestFailedOnHandler(@NonNull MediaRoute2Provider provider, long requestId, int reason) { - final int managerId = toRouterOrManagerId(requestId); - - MediaRouter2ServiceImpl service = mServiceRef.get(); - if (service == null) { + if (handleSessionCreationRequestFailed(provider, requestId, reason)) { return; } - ManagerRecord managerToNotifyFailure = null; - synchronized (service.mLock) { - for (ManagerRecord manager : mUserRecord.mManagerRecords) { - if (manager.mManagerId == managerId) { - managerToNotifyFailure = manager; - break; - } + final int requesterId = toRequesterId(requestId); + for (ManagerRecord manager : getManagerRecords()) { + if (manager.mManagerId == requesterId) { + notifyRequestFailedToManager( + manager.mManager, toOriginalRequestId(requestId), reason); + return; } } - if (managerToNotifyFailure == null) { - Slog.w(TAG, "No matching managerRecord found for managerId=" + managerId); - return; + // Currently, only the manager can get notified of failures. + // TODO: Notify router too when the related callback is introduced. + } + + // TODO: Find a way to prevent providers from notifying error on random requestId. + // Solutions can be: + // 1) Record the other type of requests too (not only session creation request) + // 2) Throw exception on providers when they try to notify error on random requestId. + private boolean handleSessionCreationRequestFailed(@NonNull MediaRoute2Provider provider, + long requestId, int reason) { + // Check whether the failure is about creating a session + SessionCreationRequest matchingRequest = null; + for (SessionCreationRequest request : mSessionCreationRequests) { + if (request.mRequestId == requestId && TextUtils.equals( + request.mRoute.getProviderId(), provider.getUniqueId())) { + matchingRequest = request; + break; + } + } + + if (matchingRequest == null) { + // The failure is not about creating a session. + return false; } - notifyRequestFailedToManager( - managerToNotifyFailure.mManager, toOriginalRequestId(requestId), reason); + mSessionCreationRequests.remove(matchingRequest); + + // Notify the requester about the failure. + // The call should be made by either MediaRouter2 or MediaRouter2Manager. + if (matchingRequest.mRequestedManagerRecord == null) { + notifySessionCreationFailedToRouter( + matchingRequest.mRouterRecord, toOriginalRequestId(requestId)); + } else { + notifyRequestFailedToManager(matchingRequest.mRequestedManagerRecord.mManager, + toOriginalRequestId(requestId), reason); + } + return true; } private void notifySessionCreatedToRouter(@NonNull RouterRecord routerRecord, @@ -1596,6 +1596,16 @@ class MediaRouter2ServiceImpl { return managers; } + private List<ManagerRecord> getManagerRecords() { + MediaRouter2ServiceImpl service = mServiceRef.get(); + if (service == null) { + return new ArrayList<>(); + } + synchronized (service.mLock) { + return new ArrayList<>(mUserRecord.mManagerRecords); + } + } + private void notifyRoutesToRouter(@NonNull IMediaRouter2 router) { List<MediaRoute2Info> routes = new ArrayList<>(); for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) { @@ -1789,12 +1799,16 @@ class MediaRouter2ServiceImpl { final class SessionCreationRequest { public final RouterRecord mRouterRecord; + public final ManagerRecord mRequestedManagerRecord; public final MediaRoute2Info mRoute; public final long mRequestId; + // requestedManagerRecord is not null only when the request is made by manager. SessionCreationRequest(@NonNull RouterRecord routerRecord, + @Nullable ManagerRecord requestedManagerRecord, @NonNull MediaRoute2Info route, long requestId) { mRouterRecord = routerRecord; + mRequestedManagerRecord = requestedManagerRecord; mRoute = route; mRequestId = requestId; } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index da9c27e5bf0a..1922c8cfbbad 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -127,7 +127,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { @Override public void requestCreateSession(String packageName, String routeId, long requestId, Bundle sessionHints) { - // Do nothing + // Handle it as an internal transfer. + transferToRoute(SYSTEM_SESSION_ID, routeId, requestId); } @Override diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b5e7ea0731f3..e7553b6bcea7 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2189,19 +2189,19 @@ public class NotificationManagerService extends SystemService { private void registerNotificationPreferencesPullers() { mPullAtomCallback = new StatsPullAtomCallbackImpl(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( PACKAGE_NOTIFICATION_PREFERENCES, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), mPullAtomCallback ); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), mPullAtomCallback ); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b547105d20a4..799ce65669db 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18562,9 +18562,10 @@ public class PackageManagerService extends IPackageManager.Stub Slog.d(TAG, "Updating package:" + ps.name + " install state for user:" + nextUserId); } - - destroyAppDataLIF(pkg, nextUserId, - FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); + if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { + destroyAppDataLIF(pkg, nextUserId, + FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); + } clearDefaultBrowserIfNeededForUser(ps.name, nextUserId); removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), nextUserId, ps.appId); clearPackagePreferredActivities(ps.name, nextUserId); 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 612989f76cd3..32cff3b2eea9 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -182,7 +182,7 @@ public class StatsPullAtomService extends SystemService { * How long to wait on an individual subsystem to return its stats. */ private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000; - private static final long NS_PER_SEC = 1000000000; + private static final long MILLIS_PER_SEC = 1000; private static final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L; private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000; @@ -680,7 +680,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {2, 3, 4, 5}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -772,7 +772,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {3, 4, 5, 6}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -810,7 +810,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {2, 3, 4, 5}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -848,7 +848,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {3, 4, 5, 6}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -886,7 +886,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {2, 3}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -955,7 +955,7 @@ public class StatsPullAtomService extends SystemService { private void registerKernelWakelock() { int tagId = FrameworkStatsLog.KERNEL_WAKELOCK; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, /* PullAtomMetadata */ null, BackgroundThread.getExecutor(), @@ -986,7 +986,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {3}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -1017,7 +1017,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {2, 3}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -1046,7 +1046,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {4}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -1078,7 +1078,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {2}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -1105,7 +1105,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {3}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -1130,7 +1130,7 @@ public class StatsPullAtomService extends SystemService { private void registerWifiActivityInfo() { int tagId = FrameworkStatsLog.WIFI_ACTIVITY_INFO; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1182,7 +1182,7 @@ public class StatsPullAtomService extends SystemService { private void registerModemActivityInfo() { int tagId = FrameworkStatsLog.MODEM_ACTIVITY_INFO; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1220,7 +1220,7 @@ public class StatsPullAtomService extends SystemService { private void registerBluetoothActivityInfo() { int tagId = FrameworkStatsLog.BLUETOOTH_ACTIVITY_INFO; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, /* metadata */ null, BackgroundThread.getExecutor(), @@ -1249,10 +1249,10 @@ public class StatsPullAtomService extends SystemService { private void registerSystemElapsedRealtime() { int tagId = FrameworkStatsLog.SYSTEM_ELAPSED_REALTIME; PullAtomMetadata metadata = new PullAtomMetadata.Builder() - .setCoolDownNs(NS_PER_SEC) - .setTimeoutNs(NS_PER_SEC / 2) + .setCoolDownMillis(MILLIS_PER_SEC) + .setTimeoutMillis(MILLIS_PER_SEC / 2) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -1271,7 +1271,7 @@ public class StatsPullAtomService extends SystemService { private void registerSystemUptime() { int tagId = FrameworkStatsLog.SYSTEM_UPTIME; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1293,7 +1293,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {4, 5, 6, 7, 8}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -1336,7 +1336,7 @@ public class StatsPullAtomService extends SystemService { private void registerProcessMemoryHighWaterMark() { int tagId = FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1389,7 +1389,7 @@ public class StatsPullAtomService extends SystemService { private void registerProcessMemorySnapshot() { int tagId = FrameworkStatsLog.PROCESS_MEMORY_SNAPSHOT; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1449,7 +1449,7 @@ public class StatsPullAtomService extends SystemService { private void registerSystemIonHeapSize() { int tagId = FrameworkStatsLog.SYSTEM_ION_HEAP_SIZE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1472,7 +1472,7 @@ public class StatsPullAtomService extends SystemService { return; } int tagId = FrameworkStatsLog.ION_HEAP_SIZE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, /* PullAtomMetadata */ null, BackgroundThread.getExecutor(), @@ -1492,7 +1492,7 @@ public class StatsPullAtomService extends SystemService { private void registerProcessSystemIonHeapSize() { int tagId = FrameworkStatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1518,7 +1518,7 @@ public class StatsPullAtomService extends SystemService { private void registerTemperature() { int tagId = FrameworkStatsLog.TEMPERATURE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1556,7 +1556,7 @@ public class StatsPullAtomService extends SystemService { private void registerCoolingDevice() { int tagId = FrameworkStatsLog.COOLING_DEVICE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1596,7 +1596,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {4, 5, 6, 8, 12}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -1639,7 +1639,7 @@ public class StatsPullAtomService extends SystemService { private void registerBinderCallsStatsExceptions() { int tagId = FrameworkStatsLog.BINDER_CALLS_EXCEPTIONS; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1674,7 +1674,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {5, 6, 7, 8, 9}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -1716,7 +1716,7 @@ public class StatsPullAtomService extends SystemService { private void registerDiskStats() { int tagId = FrameworkStatsLog.DISK_STATS; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1782,7 +1782,7 @@ public class StatsPullAtomService extends SystemService { private void registerDirectoryUsage() { int tagId = FrameworkStatsLog.DIRECTORY_USAGE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1823,7 +1823,7 @@ public class StatsPullAtomService extends SystemService { private void registerAppSize() { int tagId = FrameworkStatsLog.APP_SIZE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1867,7 +1867,7 @@ public class StatsPullAtomService extends SystemService { private void registerCategorySize() { int tagId = FrameworkStatsLog.CATEGORY_SIZE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1971,7 +1971,7 @@ public class StatsPullAtomService extends SystemService { private void registerNumFingerprintsEnrolled() { int tagId = FrameworkStatsLog.NUM_FINGERPRINTS_ENROLLED; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -1981,7 +1981,7 @@ public class StatsPullAtomService extends SystemService { private void registerNumFacesEnrolled() { int tagId = FrameworkStatsLog.NUM_FACES_ENROLLED; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2039,7 +2039,7 @@ public class StatsPullAtomService extends SystemService { private void registerProcStats() { int tagId = FrameworkStatsLog.PROC_STATS; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2049,7 +2049,7 @@ public class StatsPullAtomService extends SystemService { private void registerProcStatsPkgProc() { int tagId = FrameworkStatsLog.PROC_STATS_PKG_PROC; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2120,9 +2120,9 @@ public class StatsPullAtomService extends SystemService { int tagId = FrameworkStatsLog.DISK_IO; PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {2, 3, 4, 5, 6, 7, 8, 9, 10, 11}) - .setCoolDownNs(3 * NS_PER_SEC) + .setCoolDownMillis(3 * MILLIS_PER_SEC) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -2155,7 +2155,7 @@ public class StatsPullAtomService extends SystemService { private void registerPowerProfile() { int tagId = FrameworkStatsLog.POWER_PROFILE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, /* PullAtomMetadata */ null, BackgroundThread.getExecutor(), @@ -2180,9 +2180,9 @@ public class StatsPullAtomService extends SystemService { int tagId = FrameworkStatsLog.PROCESS_CPU_TIME; // Min cool-down is 5 sec, in line with what ActivityManagerService uses. PullAtomMetadata metadata = new PullAtomMetadata.Builder() - .setCoolDownNs(5 * NS_PER_SEC) + .setCoolDownMillis(5 * MILLIS_PER_SEC) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -2217,7 +2217,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {7, 9, 11, 13, 15, 17, 19, 21}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -2310,7 +2310,7 @@ public class StatsPullAtomService extends SystemService { private void registerDeviceCalculatedPowerUse() { int tagId = FrameworkStatsLog.DEVICE_CALCULATED_POWER_USE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2330,7 +2330,7 @@ public class StatsPullAtomService extends SystemService { private void registerDeviceCalculatedPowerBlameUid() { int tagId = FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_UID; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2360,7 +2360,7 @@ public class StatsPullAtomService extends SystemService { private void registerDeviceCalculatedPowerBlameOther() { int tagId = FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2396,7 +2396,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {1, 2, 3, 4}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -2447,7 +2447,7 @@ public class StatsPullAtomService extends SystemService { PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[] {1, 2, 3, 4}) .build(); - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, metadata, BackgroundThread.getExecutor(), @@ -2485,7 +2485,7 @@ public class StatsPullAtomService extends SystemService { private void registerBuildInformation() { int tagId = FrameworkStatsLog.BUILD_INFORMATION; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2512,7 +2512,7 @@ public class StatsPullAtomService extends SystemService { private void registerRoleHolder() { int tagId = FrameworkStatsLog.ROLE_HOLDER; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2570,7 +2570,7 @@ public class StatsPullAtomService extends SystemService { private void registerDangerousPermissionState() { int tagId = FrameworkStatsLog.DANGEROUS_PERMISSION_STATE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2660,7 +2660,7 @@ public class StatsPullAtomService extends SystemService { private void registerTimeZoneDataInfo() { int tagId = FrameworkStatsLog.TIME_ZONE_DATA_INFO; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2687,7 +2687,7 @@ public class StatsPullAtomService extends SystemService { private void registerExternalStorageInfo() { int tagId = FrameworkStatsLog.EXTERNAL_STORAGE_INFO; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2737,7 +2737,7 @@ public class StatsPullAtomService extends SystemService { private void registerAppsOnExternalStorageInfo() { int tagId = FrameworkStatsLog.APPS_ON_EXTERNAL_STORAGE_INFO; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2793,7 +2793,7 @@ public class StatsPullAtomService extends SystemService { private void registerFaceSettings() { int tagId = FrameworkStatsLog.FACE_SETTINGS; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2847,7 +2847,7 @@ public class StatsPullAtomService extends SystemService { private void registerAppOps() { int tagId = FrameworkStatsLog.APP_OPS; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2857,7 +2857,7 @@ public class StatsPullAtomService extends SystemService { private void registerRuntimeAppOpAccessMessage() { int tagId = FrameworkStatsLog.RUNTIME_APP_OP_ACCESS; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -2927,7 +2927,7 @@ public class StatsPullAtomService extends SystemService { private void registerAppFeaturesOps() { int tagId = FrameworkStatsLog.APP_FEATURES_OPS; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -3080,7 +3080,7 @@ public class StatsPullAtomService extends SystemService { private void registerNotificationRemoteViews() { int tagId = FrameworkStatsLog.NOTIFICATION_REMOTE_VIEWS; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -3124,7 +3124,7 @@ public class StatsPullAtomService extends SystemService { private void registerDangerousPermissionStateSampled() { int tagId = FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -3134,7 +3134,7 @@ public class StatsPullAtomService extends SystemService { private void registerBatteryLevel() { int tagId = FrameworkStatsLog.BATTERY_LEVEL; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -3144,7 +3144,7 @@ public class StatsPullAtomService extends SystemService { private void registerRemainingBatteryCapacity() { int tagId = FrameworkStatsLog.REMAINING_BATTERY_CAPACITY; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -3154,7 +3154,7 @@ public class StatsPullAtomService extends SystemService { private void registerFullBatteryCapacity() { int tagId = FrameworkStatsLog.FULL_BATTERY_CAPACITY; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -3164,7 +3164,7 @@ public class StatsPullAtomService extends SystemService { private void registerBatteryVoltage() { int tagId = FrameworkStatsLog.BATTERY_VOLTAGE; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), @@ -3174,7 +3174,7 @@ public class StatsPullAtomService extends SystemService { private void registerBatteryCycleCount() { int tagId = FrameworkStatsLog.BATTERY_CYCLE_COUNT; - mStatsManager.registerPullAtomCallback( + mStatsManager.setPullAtomCallback( tagId, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 61a33b4fb781..d7a80bfd7928 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1392,7 +1392,7 @@ final class AccessibilityController { tempWindowStatesList.add(w); } }, false /* traverseTopToBottom */); - // Insert the re-parented windows in another display on top of their parents in + // Insert the re-parented windows in another display below their parents in // default display. mService.mRoot.forAllWindows(w -> { final WindowState parentWindow = findRootDisplayParentWindow(w); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index acb9900bafe9..7a302110b13b 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7628,6 +7628,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } @Override + boolean canCreateRemoteAnimationTarget() { + return true; + } + + @Override void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets, Rect outSurfaceInsets) { final WindowState win = findMainWindow(); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 0912b2e8b52f..6a47c9e217f8 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -446,7 +446,7 @@ public class AppTransitionController { siblings.add(current); boolean canPromote = true; - if (parent == null) { + if (parent == null || !parent.canCreateRemoteAnimationTarget()) { canPromote = false; } else { // In case a descendant of the parent belongs to the other group, we cannot promote diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 3b3234a4bec0..19f8ca912449 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -379,10 +379,8 @@ class SurfaceAnimator { final SurfaceControl.Builder builder = animatable.makeAnimationLeash() .setParent(animatable.getAnimationLeashParent()) .setHidden(hidden) - .setName(surface + " - animation-leash") - .setColorLayer(); + .setName(surface + " - animation-leash"); final SurfaceControl leash = builder.build(); - t.unsetColor(leash); t.setWindowCrop(leash, width, height); t.setPosition(leash, x, y); t.show(leash); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index faa6bac8e623..88d4fb31b16b 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3121,6 +3121,11 @@ class Task extends WindowContainer<WindowContainer> { return activity != null ? activity.createRemoteAnimationTarget(record) : null; } + @Override + boolean canCreateRemoteAnimationTarget() { + return true; + } + WindowState getTopVisibleAppMainWindow() { final ActivityRecord activity = getTopVisibleActivity(); return activity != null ? activity.findMainWindow() : null; diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 8f5d04891742..b38c18bf15f3 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -463,8 +463,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub int configMask = change.getConfigSetMask(); int windowMask = change.getWindowSetMask(); configMask &= ActivityInfo.CONFIG_WINDOW_CONFIGURATION - | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; - windowMask &= WindowConfiguration.WINDOW_CONFIG_BOUNDS; + | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE; + windowMask &= (WindowConfiguration.WINDOW_CONFIG_BOUNDS + | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS); int effects = 0; if (configMask != 0) { Configuration c = new Configuration(container.getRequestedOverrideConfiguration()); diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java index 74d5c338a68d..205b423a38dd 100644 --- a/services/core/java/com/android/server/wm/TaskTile.java +++ b/services/core/java/com/android/server/wm/TaskTile.java @@ -147,7 +147,7 @@ public class TaskTile extends ActivityStack { */ void updateResolvedConfig(Configuration inOutResolvedConfig) { Rect resolveBounds = inOutResolvedConfig.windowConfiguration.getBounds(); - if (resolveBounds == null || resolveBounds.isEmpty()) { + if (resolveBounds.isEmpty()) { resolveBounds.set(getRequestedOverrideBounds()); } int stackMode = inOutResolvedConfig.windowConfiguration.getWindowingMode(); @@ -162,6 +162,17 @@ public class TaskTile extends ActivityStack { inOutResolvedConfig.smallestScreenWidthDp = getRequestedOverrideConfiguration().smallestScreenWidthDp; } + if (inOutResolvedConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { + inOutResolvedConfig.screenWidthDp = getRequestedOverrideConfiguration().screenWidthDp; + } + if (inOutResolvedConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { + inOutResolvedConfig.screenHeightDp = getRequestedOverrideConfiguration().screenHeightDp; + } + Rect resolveAppBounds = inOutResolvedConfig.windowConfiguration.getAppBounds(); + if (resolveAppBounds == null || resolveAppBounds.isEmpty()) { + inOutResolvedConfig.windowConfiguration.setAppBounds( + getRequestedOverrideConfiguration().windowConfiguration.getAppBounds()); + } } @Override @@ -184,7 +195,6 @@ public class TaskTile extends ActivityStack { boolean isResizable = topTask == null || topTask.isResizeable(); info.resizeMode = isResizable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE; info.topActivityType = top == null ? ACTIVITY_TYPE_UNDEFINED : top.getActivityType(); - info.configuration.setTo(getRequestedOverrideConfiguration()); } @Override diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index b12c6980ccce..10d34b57bacb 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2196,6 +2196,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return null; } + boolean canCreateRemoteAnimationTarget() { + return false; + } + boolean okToDisplay() { final DisplayContent dc = getDisplayContent(); return dc != null && dc.okToDisplay(); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index f140ce88a363..27f1ca025a93 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -657,7 +657,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private boolean mIsDimming = false; private @Nullable InsetsSourceProvider mControllableInsetProvider; - private InsetsState mRequestedInsetsState; + private final InsetsState mRequestedInsetsState = new InsetsState(); private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; private KeyInterceptionInfo mKeyInterceptionInfo; @@ -816,9 +816,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mSeq = seq; mPowerManagerWrapper = powerManagerWrapper; mForceSeamlesslyRotate = token.mRoundedCornerOverlay; - mRequestedInsetsState = new InsetsState( - getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this), - true /* copySources */); if (DEBUG) { Slog.v(TAG, "Window " + this + " client=" + c.asBinder() + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a); diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd index a70568f5911a..5a4c682f52eb 100644 --- a/services/core/xsd/platform-compat-config.xsd +++ b/services/core/xsd/platform-compat-config.xsd @@ -27,6 +27,7 @@ <xs:attribute type="xs:long" name="id" use="required"/> <xs:attribute type="xs:string" name="name" use="required"/> <xs:attribute type="xs:boolean" name="disabled"/> + <xs:attribute type="xs:boolean" name="loggingOnly"/> <xs:attribute type="xs:int" name="enableAfterTargetSdk"/> <xs:attribute type="xs:string" name="description"/> </xs:extension> diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt index 3a33f63361a5..7def58d56a26 100644 --- a/services/core/xsd/platform-compat-schema/current.txt +++ b/services/core/xsd/platform-compat-schema/current.txt @@ -7,12 +7,14 @@ package com.android.server.compat.config { method public boolean getDisabled(); method public int getEnableAfterTargetSdk(); method public long getId(); + method public boolean getLoggingOnly(); method public String getName(); method public String getValue(); method public void setDescription(String); method public void setDisabled(boolean); method public void setEnableAfterTargetSdk(int); method public void setId(long); + method public void setLoggingOnly(boolean); method public void setName(String); method public void setValue(String); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1de704d1239d..c58eae1cf330 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2691,6 +2691,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + /** + * If the device is in Device Owner mode, apply the restriction on adding + * a managed profile. + */ + @GuardedBy("getLockObject()") + void applyManagedProfileRestrictionIfDeviceOwnerLocked() { + final int doUserId = mOwners.getDeviceOwnerUserId(); + if (doUserId == UserHandle.USER_NULL) { + logIfVerbose("No DO found, skipping application of restriction."); + return; + } + + final UserHandle doUserHandle = UserHandle.of(doUserId); + // Set the restriction if not set. + if (!mUserManager.hasUserRestriction( + UserManager.DISALLOW_ADD_MANAGED_PROFILE, doUserHandle)) { + mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true, + doUserHandle); + } + } + /** Apply default restrictions that haven't been applied to profile owners yet. */ private void maybeSetDefaultProfileOwnerUserRestrictions() { synchronized (getLockObject()) { @@ -3901,6 +3922,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { maybeStartSecurityLogMonitorOnActivityManagerReady(); synchronized (getLockObject()) { migrateToProfileOnOrganizationOwnedDeviceIfCompLocked(); + applyManagedProfileRestrictionIfDeviceOwnerLocked(); } final int userId = getManagedUserId(UserHandle.USER_SYSTEM); if (userId >= 0) { @@ -8764,6 +8786,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mOwners.writeProfileOwner(userId); deleteTransferOwnershipBundleLocked(userId); toggleBackupServiceActive(userId, true); + applyManagedProfileRestrictionIfDeviceOwnerLocked(); } @Override diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 983a639776f6..37bf66491882 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -72,13 +72,13 @@ public class PeopleService extends SystemService { } @Override - public void onUserUnlocking(@NonNull TargetUser targetUser) { - mDataManager.onUserUnlocked(targetUser.getUserIdentifier()); + public void onUserUnlocked(@NonNull TargetUser user) { + mDataManager.onUserUnlocked(user.getUserIdentifier()); } @Override - public void onUserStopping(@NonNull TargetUser targetUser) { - mDataManager.onUserStopped(targetUser.getUserIdentifier()); + public void onUserStopping(@NonNull TargetUser user) { + mDataManager.onUserStopping(user.getUserIdentifier()); } @VisibleForTesting 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 e4ce6bafff31..4e0afc525383 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -198,13 +198,13 @@ public class DataManager { DataMaintenanceService.scheduleJob(mContext, userId); } - /** This method is called when a user is stopped. */ - public void onUserStopped(int 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.valueAt(userId).cancel(true); + mUsageStatsQueryFutures.get(userId).cancel(true); } if (mBroadcastReceivers.indexOfKey(userId) >= 0) { mContext.unregisterReceiver(mBroadcastReceivers.get(userId)); diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java index 328c71dbc7db..2cbe7be8ac33 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java @@ -40,52 +40,57 @@ class CompatConfigBuilder { } CompatConfigBuilder addTargetSdkChangeWithId(int sdk, long id) { - mChanges.add(new CompatChange(id, "", sdk, false, "")); + mChanges.add(new CompatChange(id, "", sdk, false, false, "")); return this; } CompatConfigBuilder addTargetSdkDisabledChangeWithId(int sdk, long id) { - mChanges.add(new CompatChange(id, "", sdk, true, "")); + mChanges.add(new CompatChange(id, "", sdk, true, false, "")); return this; } CompatConfigBuilder addTargetSdkChangeWithIdAndName(int sdk, long id, String name) { - mChanges.add(new CompatChange(id, name, sdk, false, "")); + mChanges.add(new CompatChange(id, name, sdk, false, false, "")); return this; } CompatConfigBuilder addTargetSdkChangeWithIdAndDescription(int sdk, long id, String description) { - mChanges.add(new CompatChange(id, "", sdk, false, description)); + mChanges.add(new CompatChange(id, "", sdk, false, false, description)); return this; } CompatConfigBuilder addEnabledChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, false, "")); + mChanges.add(new CompatChange(id, "", -1, false, false, "")); return this; } CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) { - mChanges.add(new CompatChange(id, name, -1, false, "")); + mChanges.add(new CompatChange(id, name, -1, false, false, "")); return this; } CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) { - mChanges.add(new CompatChange(id, "", -1, false, description)); + mChanges.add(new CompatChange(id, "", -1, false, false, description)); return this; } CompatConfigBuilder addDisabledChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, true, "")); + mChanges.add(new CompatChange(id, "", -1, true, false, "")); return this; } CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) { - mChanges.add(new CompatChange(id, name, -1, true, "")); + mChanges.add(new CompatChange(id, name, -1, true, false, "")); return this; } CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) { - mChanges.add(new CompatChange(id, "", -1, true, description)); + mChanges.add(new CompatChange(id, "", -1, true, false, description)); + return this; + } + + CompatConfigBuilder addLoggingOnlyChangeWithId(long id) { + mChanges.add(new CompatChange(id, "", -1, false, true, "")); return this; } 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 44f4ccf69cdd..eb2dd6461071 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -214,6 +214,17 @@ public class CompatConfigTest { } @Test + public void testLoggingOnlyChangePreventAddOverride() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addLoggingOnlyChangeWithId(1234L) + .build(); + + assertThrows(SecurityException.class, + () -> compatConfig.addOverride(1234L, "com.some.package", true) + ); + } + + @Test public void testPreventRemoveOverride() throws Exception { CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) .addDisabledChangeWithId(1234L) diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java index b14291b34195..16291b256c44 100644 --- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java @@ -20,6 +20,7 @@ import static com.android.internal.compat.OverrideAllowedState.ALLOWED; import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK; import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH; +import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE; import static com.google.common.truth.Truth.assertThat; @@ -78,6 +79,7 @@ public class OverrideValidatorImplTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + android.app.compat.ChangeIdStateCache.disable(); when(mContext.getPackageManager()).thenReturn(mPackageManager); } @@ -89,7 +91,8 @@ public class OverrideValidatorImplTest { .addTargetSdkChangeWithId(TARGET_SDK, 2) .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) .addEnabledChangeWithId(4) - .addDisabledChangeWithId(5).build(); + .addDisabledChangeWithId(5) + .addLoggingOnlyChangeWithId(6).build(); IOverrideValidator overrideValidator = config.getOverrideValidator(); when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) .thenReturn(ApplicationInfoBuilder.create() @@ -107,6 +110,8 @@ public class OverrideValidatorImplTest { overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); OverrideAllowedState stateDisabledChange = overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + OverrideAllowedState stateDLoggingOnlyChange = + overrideValidator.getOverrideAllowedState(6, PACKAGE_NAME); assertThat(stateTargetSdkLessChange) .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); @@ -118,6 +123,8 @@ public class OverrideValidatorImplTest { .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); assertThat(stateDisabledChange) .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateDLoggingOnlyChange) + .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1)); } @Test @@ -128,7 +135,8 @@ public class OverrideValidatorImplTest { .addTargetSdkChangeWithId(TARGET_SDK, 2) .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) .addEnabledChangeWithId(4) - .addDisabledChangeWithId(5).build(); + .addDisabledChangeWithId(5) + .addLoggingOnlyChangeWithId(6).build(); IOverrideValidator overrideValidator = config.getOverrideValidator(); when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) .thenReturn(ApplicationInfoBuilder.create() @@ -145,6 +153,8 @@ public class OverrideValidatorImplTest { overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); OverrideAllowedState stateDisabledChange = overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + OverrideAllowedState stateDLoggingOnlyChange = + overrideValidator.getOverrideAllowedState(6, PACKAGE_NAME); assertThat(stateTargetSdkLessChange) .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); @@ -156,6 +166,8 @@ public class OverrideValidatorImplTest { .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); assertThat(stateDisabledChange) .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateDLoggingOnlyChange) + .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1)); } @Test @@ -232,7 +244,8 @@ public class OverrideValidatorImplTest { .addTargetSdkChangeWithId(TARGET_SDK, 2) .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) .addEnabledChangeWithId(4) - .addDisabledChangeWithId(5).build(); + .addDisabledChangeWithId(5) + .addLoggingOnlyChangeWithId(6).build(); IOverrideValidator overrideValidator = config.getOverrideValidator(); when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) .thenReturn(ApplicationInfoBuilder.create() @@ -249,6 +262,8 @@ public class OverrideValidatorImplTest { overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); OverrideAllowedState stateDisabledChange = overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + OverrideAllowedState stateDLoggingOnlyChange = + overrideValidator.getOverrideAllowedState(6, PACKAGE_NAME); assertThat(stateTargetSdkLessChange) .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); @@ -260,6 +275,8 @@ public class OverrideValidatorImplTest { .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); assertThat(stateDisabledChange) .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateDLoggingOnlyChange) + .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1)); } @Test @@ -351,7 +368,8 @@ public class OverrideValidatorImplTest { .addTargetSdkChangeWithId(TARGET_SDK, 2) .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) .addEnabledChangeWithId(4) - .addDisabledChangeWithId(5).build(); + .addDisabledChangeWithId(5) + .addLoggingOnlyChangeWithId(6).build(); IOverrideValidator overrideValidator = config.getOverrideValidator(); when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) .thenReturn(ApplicationInfoBuilder.create() @@ -368,6 +386,8 @@ public class OverrideValidatorImplTest { overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); OverrideAllowedState stateDisabledChange = overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + OverrideAllowedState stateDLoggingOnlyChange = + overrideValidator.getOverrideAllowedState(6, PACKAGE_NAME); assertThat(stateTargetSdkLessChange) .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); @@ -379,5 +399,7 @@ public class OverrideValidatorImplTest { .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); assertThat(stateDisabledChange) .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateDLoggingOnlyChange) + .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1)); } } diff --git a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java index 891ca74a545f..0e4d2be49b0e 100644 --- a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java +++ b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java @@ -16,30 +16,52 @@ package com.android.server.content; -import java.util.ArrayList; - +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.content.ContentResolver; import android.database.ContentObserver; +import android.database.IContentObserver; import android.net.Uri; +import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.util.ArraySet; + +import androidx.test.runner.AndroidJUnit4; -import com.android.server.content.ContentService.ObserverCall; +import com.android.server.LocalServices; +import com.android.server.content.ContentService.ObserverCollector; import com.android.server.content.ContentService.ObserverNode; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; + +import java.util.Arrays; + /** * atest FrameworksServicesTests:com.android.server.content.ObserverNodeTest */ -@SmallTest -public class ObserverNodeTest extends AndroidTestCase { - static class TestObserver extends ContentObserver { +@RunWith(AndroidJUnit4.class) +public class ObserverNodeTest { + static class TestObserver extends ContentObserver { public TestObserver() { super(new Handler(Looper.getMainLooper())); } } + @Test public void testUri() { final int myUserHandle = UserHandle.myUserId(); @@ -65,15 +87,15 @@ public class ObserverNodeTest extends AndroidTestCase { 0, 0, myUserHandle); } - ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); - for (int i = nums.length - 1; i >=0; --i) { - root.collectObserversLocked(uris[i], 0, null, false, 0, myUserHandle, calls); - assertEquals(nums[i], calls.size()); - calls.clear(); + final ObserverCollector collector = mock(ObserverCollector.class); + root.collectObserversLocked(uris[i], 0, null, false, 0, myUserHandle, collector); + verify(collector, times(nums[i])).collect( + any(), anyInt(), anyBoolean(), any(), anyInt(), anyInt()); } } + @Test public void testUriNotNotify() { final int myUserHandle = UserHandle.myUserId(); @@ -95,12 +117,67 @@ public class ObserverNodeTest extends AndroidTestCase { 0, 0, myUserHandle); } - ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); - for (int i = uris.length - 1; i >=0; --i) { - root.collectObserversLocked(uris[i], 0, null, false, 0, myUserHandle, calls); - assertEquals(nums[i], calls.size()); - calls.clear(); + final ObserverCollector collector = mock(ObserverCollector.class); + root.collectObserversLocked(uris[i], 0, null, false, 0, myUserHandle, collector); + verify(collector, times(nums[i])).collect( + any(), anyInt(), anyBoolean(), any(), anyInt(), anyInt()); + } + } + + @Test + public void testCluster() throws Exception { + final int myUserHandle = UserHandle.myUserId(); + + // Assume everything is foreground during our test + final ActivityManagerInternal ami = mock(ActivityManagerInternal.class); + when(ami.getUidProcessState(anyInt())) + .thenReturn(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + LocalServices.addService(ActivityManagerInternal.class, ami); + + final IContentObserver observer = mock(IContentObserver.class); + when(observer.asBinder()).thenReturn(new Binder()); + + final ObserverNode root = new ObserverNode(""); + root.addObserverLocked(Uri.parse("content://authority/"), observer, + true, root, 0, 1000, myUserHandle); + + final ObserverCollector collector = new ObserverCollector(); + root.collectObserversLocked(Uri.parse("content://authority/1"), 0, null, false, + 0, myUserHandle, collector); + root.collectObserversLocked(Uri.parse("content://authority/1"), 0, null, false, + ContentResolver.NOTIFY_INSERT, myUserHandle, collector); + root.collectObserversLocked(Uri.parse("content://authority/2"), 0, null, false, + ContentResolver.NOTIFY_INSERT, myUserHandle, collector); + root.collectObserversLocked(Uri.parse("content://authority/2"), 0, null, false, + ContentResolver.NOTIFY_UPDATE, myUserHandle, collector); + collector.dispatch(); + + // We should only cluster when all other arguments are equal + verify(observer).onChangeEtc(eq(false), argThat(new UriSetMatcher( + Uri.parse("content://authority/1"))), + eq(0), anyInt()); + verify(observer).onChangeEtc(eq(false), argThat(new UriSetMatcher( + Uri.parse("content://authority/1"), + Uri.parse("content://authority/2"))), + eq(ContentResolver.NOTIFY_INSERT), anyInt()); + verify(observer).onChangeEtc(eq(false), argThat(new UriSetMatcher( + Uri.parse("content://authority/2"))), + eq(ContentResolver.NOTIFY_UPDATE), anyInt()); + } + + private static class UriSetMatcher implements ArgumentMatcher<Uri[]> { + private final ArraySet<Uri> uris; + + public UriSetMatcher(Uri... uris) { + this.uris = new ArraySet<>(Arrays.asList(uris)); + } + + @Override + public boolean matches(Uri[] uris) { + final ArraySet<Uri> test = new ArraySet<>(Arrays.asList(uris)); + return this.uris.equals(test); } } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java index 2e77c9fd694e..684bbd4fc8eb 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java @@ -236,6 +236,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { @Test public void testSetLockCredential_forProfileWithSeparateChallenge_updatesCredentials() throws Exception { + mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, true, null); initializeStorageWithCredential( MANAGED_PROFILE_USER_ID, newPattern("12345"), 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 624b67ccda85..a4d63ac8a564 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 @@ -269,7 +269,7 @@ public final class DataManagerTest { assertEquals(1, conversations.size()); assertEquals("sc_1", conversations.get(0).getShortcutId()); - mDataManager.onUserStopped(USER_ID_PRIMARY); + mDataManager.onUserStopping(USER_ID_PRIMARY); conversations = getConversationsInPrimary(); assertTrue(conversations.isEmpty()); } 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 e501452a2df2..48a583cad7a3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -39,6 +39,7 @@ 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.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -253,6 +254,30 @@ public class TaskOrganizerTests extends WindowTestsBase { } @Test + public void testOverrideConfigSize() { + removeGlobalMinSizeRestriction(); + final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot) + .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); + final Task task = stack.getTopMostTask(); + WindowContainerTransaction t = new WindowContainerTransaction(); + t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100)); + mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + final int origScreenWDp = task.getConfiguration().screenHeightDp; + final int origScreenHDp = task.getConfiguration().screenHeightDp; + t = new WindowContainerTransaction(); + // verify that setting config overrides on parent restricts children. + t.setScreenSizeDp(stack.mRemoteToken, origScreenWDp, origScreenHDp); + t.setBounds(task.mRemoteToken, new Rect(10, 10, 150, 200)); + mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp); + t = new WindowContainerTransaction(); + t.setScreenSizeDp(stack.mRemoteToken, Configuration.SCREEN_WIDTH_DP_UNDEFINED, + Configuration.SCREEN_HEIGHT_DP_UNDEFINED); + mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + assertNotEquals(origScreenHDp, task.getConfiguration().screenHeightDp); + } + + @Test public void testCreateDeleteRootTasks() { RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( Display.DEFAULT_DISPLAY, diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index 01abb2661f39..558f4cd24471 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -19,6 +19,10 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.hardware.radio.V1_1.EutranBands; +import android.hardware.radio.V1_1.GeranBands; +import android.hardware.radio.V1_5.AccessNetwork; +import android.hardware.radio.V1_5.UtranBands; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -84,13 +88,13 @@ public final class AccessNetworkConstants { public @interface RadioAccessNetworkType {} public static final class AccessNetworkType { - public static final int UNKNOWN = 0; - public static final int GERAN = 1; - public static final int UTRAN = 2; - public static final int EUTRAN = 3; - public static final int CDMA2000 = 4; - public static final int IWLAN = 5; - public static final int NGRAN = 6; + public static final int UNKNOWN = AccessNetwork.UNKNOWN; + public static final int GERAN = AccessNetwork.GERAN; + public static final int UTRAN = AccessNetwork.UTRAN; + public static final int EUTRAN = AccessNetwork.EUTRAN; + public static final int CDMA2000 = AccessNetwork.CDMA2000; + public static final int IWLAN = AccessNetwork.IWLAN; + public static final int NGRAN = AccessNetwork.NGRAN; /** @hide */ private AccessNetworkType() {} @@ -115,20 +119,20 @@ public final class AccessNetworkConstants { * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf */ public static final class GeranBand { - public static final int BAND_T380 = 1; - public static final int BAND_T410 = 2; - public static final int BAND_450 = 3; - public static final int BAND_480 = 4; - public static final int BAND_710 = 5; - public static final int BAND_750 = 6; - public static final int BAND_T810 = 7; - public static final int BAND_850 = 8; - public static final int BAND_P900 = 9; - public static final int BAND_E900 = 10; - public static final int BAND_R900 = 11; - public static final int BAND_DCS1800 = 12; - public static final int BAND_PCS1900 = 13; - public static final int BAND_ER900 = 14; + public static final int BAND_T380 = GeranBands.BAND_T380; + public static final int BAND_T410 = GeranBands.BAND_T410; + public static final int BAND_450 = GeranBands.BAND_450; + public static final int BAND_480 = GeranBands.BAND_480; + public static final int BAND_710 = GeranBands.BAND_710; + public static final int BAND_750 = GeranBands.BAND_750; + public static final int BAND_T810 = GeranBands.BAND_T810; + public static final int BAND_850 = GeranBands.BAND_850; + public static final int BAND_P900 = GeranBands.BAND_P900; + public static final int BAND_E900 = GeranBands.BAND_E900; + public static final int BAND_R900 = GeranBands.BAND_R900; + public static final int BAND_DCS1800 = GeranBands.BAND_DCS1800; + public static final int BAND_PCS1900 = GeranBands.BAND_PCS1900; + public static final int BAND_ER900 = GeranBands.BAND_ER900; /** @hide */ private GeranBand() {} @@ -139,28 +143,28 @@ public final class AccessNetworkConstants { * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf */ public static final class UtranBand { - public static final int BAND_1 = 1; - public static final int BAND_2 = 2; - public static final int BAND_3 = 3; - public static final int BAND_4 = 4; - public static final int BAND_5 = 5; - public static final int BAND_6 = 6; - public static final int BAND_7 = 7; - public static final int BAND_8 = 8; - public static final int BAND_9 = 9; - public static final int BAND_10 = 10; - public static final int BAND_11 = 11; - public static final int BAND_12 = 12; - public static final int BAND_13 = 13; - public static final int BAND_14 = 14; + public static final int BAND_1 = UtranBands.BAND_1; + public static final int BAND_2 = UtranBands.BAND_2; + public static final int BAND_3 = UtranBands.BAND_3; + public static final int BAND_4 = UtranBands.BAND_4; + public static final int BAND_5 = UtranBands.BAND_5; + public static final int BAND_6 = UtranBands.BAND_6; + public static final int BAND_7 = UtranBands.BAND_7; + public static final int BAND_8 = UtranBands.BAND_8; + public static final int BAND_9 = UtranBands.BAND_9; + public static final int BAND_10 = UtranBands.BAND_10; + public static final int BAND_11 = UtranBands.BAND_11; + public static final int BAND_12 = UtranBands.BAND_12; + public static final int BAND_13 = UtranBands.BAND_13; + public static final int BAND_14 = UtranBands.BAND_14; // band 15, 16, 17, 18 are reserved - public static final int BAND_19 = 19; - public static final int BAND_20 = 20; - public static final int BAND_21 = 21; - public static final int BAND_22 = 22; + public static final int BAND_19 = UtranBands.BAND_19; + public static final int BAND_20 = UtranBands.BAND_20; + public static final int BAND_21 = UtranBands.BAND_21; + public static final int BAND_22 = UtranBands.BAND_22; // band 23, 24 are reserved - public static final int BAND_25 = 25; - public static final int BAND_26 = 26; + public static final int BAND_25 = UtranBands.BAND_25; + public static final int BAND_26 = UtranBands.BAND_26; // Frequency bands for TD-SCDMA. Defined in 3GPP TS 25.102, Table 5.2. @@ -169,38 +173,38 @@ public final class AccessNetworkConstants { * 1900 - 1920 MHz: Uplink and downlink transmission * 2010 - 2025 MHz: Uplink and downlink transmission */ - public static final int BAND_A = 101; + public static final int BAND_A = UtranBands.BAND_A; /** * Band B * 1850 - 1910 MHz: Uplink and downlink transmission * 1930 - 1990 MHz: Uplink and downlink transmission */ - public static final int BAND_B = 102; + public static final int BAND_B = UtranBands.BAND_B; /** * Band C * 1910 - 1930 MHz: Uplink and downlink transmission */ - public static final int BAND_C = 103; + public static final int BAND_C = UtranBands.BAND_C; /** * Band D * 2570 - 2620 MHz: Uplink and downlink transmission */ - public static final int BAND_D = 104; + public static final int BAND_D = UtranBands.BAND_D; /** * Band E * 2300—2400 MHz: Uplink and downlink transmission */ - public static final int BAND_E = 105; + public static final int BAND_E = UtranBands.BAND_E; /** * Band F * 1880 - 1920 MHz: Uplink and downlink transmission */ - public static final int BAND_F = 106; + public static final int BAND_F = UtranBands.BAND_F; /** @hide */ private UtranBand() {} @@ -211,54 +215,54 @@ public final class AccessNetworkConstants { * http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf */ public static final class EutranBand { - public static final int BAND_1 = 1; - public static final int BAND_2 = 2; - public static final int BAND_3 = 3; - public static final int BAND_4 = 4; - public static final int BAND_5 = 5; - public static final int BAND_6 = 6; - public static final int BAND_7 = 7; - public static final int BAND_8 = 8; - public static final int BAND_9 = 9; - public static final int BAND_10 = 10; - public static final int BAND_11 = 11; - public static final int BAND_12 = 12; - public static final int BAND_13 = 13; - public static final int BAND_14 = 14; - public static final int BAND_17 = 17; - public static final int BAND_18 = 18; - public static final int BAND_19 = 19; - public static final int BAND_20 = 20; - public static final int BAND_21 = 21; - public static final int BAND_22 = 22; - public static final int BAND_23 = 23; - public static final int BAND_24 = 24; - public static final int BAND_25 = 25; - public static final int BAND_26 = 26; - public static final int BAND_27 = 27; - public static final int BAND_28 = 28; - public static final int BAND_30 = 30; - public static final int BAND_31 = 31; - public static final int BAND_33 = 33; - public static final int BAND_34 = 34; - public static final int BAND_35 = 35; - public static final int BAND_36 = 36; - public static final int BAND_37 = 37; - public static final int BAND_38 = 38; - public static final int BAND_39 = 39; - public static final int BAND_40 = 40; - public static final int BAND_41 = 41; - public static final int BAND_42 = 42; - public static final int BAND_43 = 43; - public static final int BAND_44 = 44; - public static final int BAND_45 = 45; - public static final int BAND_46 = 46; - public static final int BAND_47 = 47; - public static final int BAND_48 = 48; - public static final int BAND_65 = 65; - public static final int BAND_66 = 66; - public static final int BAND_68 = 68; - public static final int BAND_70 = 70; + public static final int BAND_1 = EutranBands.BAND_1; + public static final int BAND_2 = EutranBands.BAND_2; + public static final int BAND_3 = EutranBands.BAND_3; + public static final int BAND_4 = EutranBands.BAND_4; + public static final int BAND_5 = EutranBands.BAND_5; + public static final int BAND_6 = EutranBands.BAND_6; + public static final int BAND_7 = EutranBands.BAND_7; + public static final int BAND_8 = EutranBands.BAND_8; + public static final int BAND_9 = EutranBands.BAND_9; + public static final int BAND_10 = EutranBands.BAND_10; + public static final int BAND_11 = EutranBands.BAND_11; + public static final int BAND_12 = EutranBands.BAND_12; + public static final int BAND_13 = EutranBands.BAND_13; + public static final int BAND_14 = EutranBands.BAND_14; + public static final int BAND_17 = EutranBands.BAND_17; + public static final int BAND_18 = EutranBands.BAND_18; + public static final int BAND_19 = EutranBands.BAND_19; + public static final int BAND_20 = EutranBands.BAND_20; + public static final int BAND_21 = EutranBands.BAND_21; + public static final int BAND_22 = EutranBands.BAND_22; + public static final int BAND_23 = EutranBands.BAND_23; + public static final int BAND_24 = EutranBands.BAND_24; + public static final int BAND_25 = EutranBands.BAND_25; + public static final int BAND_26 = EutranBands.BAND_26; + public static final int BAND_27 = EutranBands.BAND_27; + public static final int BAND_28 = EutranBands.BAND_28; + public static final int BAND_30 = EutranBands.BAND_30; + public static final int BAND_31 = EutranBands.BAND_31; + public static final int BAND_33 = EutranBands.BAND_33; + public static final int BAND_34 = EutranBands.BAND_34; + public static final int BAND_35 = EutranBands.BAND_35; + public static final int BAND_36 = EutranBands.BAND_36; + public static final int BAND_37 = EutranBands.BAND_37; + public static final int BAND_38 = EutranBands.BAND_38; + public static final int BAND_39 = EutranBands.BAND_39; + public static final int BAND_40 = EutranBands.BAND_40; + public static final int BAND_41 = EutranBands.BAND_41; + public static final int BAND_42 = EutranBands.BAND_42; + public static final int BAND_43 = EutranBands.BAND_43; + public static final int BAND_44 = EutranBands.BAND_44; + public static final int BAND_45 = EutranBands.BAND_45; + public static final int BAND_46 = EutranBands.BAND_46; + public static final int BAND_47 = EutranBands.BAND_47; + public static final int BAND_48 = EutranBands.BAND_48; + public static final int BAND_65 = EutranBands.BAND_65; + public static final int BAND_66 = EutranBands.BAND_66; + public static final int BAND_68 = EutranBands.BAND_68; + public static final int BAND_70 = EutranBands.BAND_70; /** @hide */ private EutranBand() {}; @@ -304,51 +308,51 @@ public final class AccessNetworkConstants { */ public static final class NgranBands { /** FR1 bands */ - public static final int BAND_1 = 1; - public static final int BAND_2 = 2; - public static final int BAND_3 = 3; - public static final int BAND_5 = 5; - public static final int BAND_7 = 7; - public static final int BAND_8 = 8; - public static final int BAND_12 = 12; - public static final int BAND_14 = 14; - public static final int BAND_18 = 18; - public static final int BAND_20 = 20; - public static final int BAND_25 = 25; - public static final int BAND_28 = 28; - public static final int BAND_29 = 29; - public static final int BAND_30 = 30; - public static final int BAND_34 = 34; - public static final int BAND_38 = 38; - public static final int BAND_39 = 39; - public static final int BAND_40 = 40; - public static final int BAND_41 = 41; - public static final int BAND_48 = 48; - public static final int BAND_50 = 50; - public static final int BAND_51 = 51; - public static final int BAND_65 = 65; - public static final int BAND_66 = 66; - public static final int BAND_70 = 70; - public static final int BAND_71 = 71; - public static final int BAND_74 = 74; - public static final int BAND_75 = 75; - public static final int BAND_76 = 76; - public static final int BAND_77 = 77; - public static final int BAND_78 = 78; - public static final int BAND_79 = 79; - public static final int BAND_80 = 80; - public static final int BAND_81 = 81; - public static final int BAND_82 = 82; - public static final int BAND_83 = 83; - public static final int BAND_84 = 84; - public static final int BAND_86 = 86; - public static final int BAND_90 = 90; + public static final int BAND_1 = android.hardware.radio.V1_5.NgranBands.BAND_1; + public static final int BAND_2 = android.hardware.radio.V1_5.NgranBands.BAND_2; + public static final int BAND_3 = android.hardware.radio.V1_5.NgranBands.BAND_3; + public static final int BAND_5 = android.hardware.radio.V1_5.NgranBands.BAND_5; + public static final int BAND_7 = android.hardware.radio.V1_5.NgranBands.BAND_7; + public static final int BAND_8 = android.hardware.radio.V1_5.NgranBands.BAND_8; + public static final int BAND_12 = android.hardware.radio.V1_5.NgranBands.BAND_12; + public static final int BAND_14 = android.hardware.radio.V1_5.NgranBands.BAND_14; + public static final int BAND_18 = android.hardware.radio.V1_5.NgranBands.BAND_18; + public static final int BAND_20 = android.hardware.radio.V1_5.NgranBands.BAND_20; + public static final int BAND_25 = android.hardware.radio.V1_5.NgranBands.BAND_25; + public static final int BAND_28 = android.hardware.radio.V1_5.NgranBands.BAND_28; + public static final int BAND_29 = android.hardware.radio.V1_5.NgranBands.BAND_29; + public static final int BAND_30 = android.hardware.radio.V1_5.NgranBands.BAND_30; + public static final int BAND_34 = android.hardware.radio.V1_5.NgranBands.BAND_34; + public static final int BAND_38 = android.hardware.radio.V1_5.NgranBands.BAND_38; + public static final int BAND_39 = android.hardware.radio.V1_5.NgranBands.BAND_39; + public static final int BAND_40 = android.hardware.radio.V1_5.NgranBands.BAND_40; + public static final int BAND_41 = android.hardware.radio.V1_5.NgranBands.BAND_41; + public static final int BAND_48 = android.hardware.radio.V1_5.NgranBands.BAND_48; + public static final int BAND_50 = android.hardware.radio.V1_5.NgranBands.BAND_50; + public static final int BAND_51 = android.hardware.radio.V1_5.NgranBands.BAND_51; + public static final int BAND_65 = android.hardware.radio.V1_5.NgranBands.BAND_65; + public static final int BAND_66 = android.hardware.radio.V1_5.NgranBands.BAND_66; + public static final int BAND_70 = android.hardware.radio.V1_5.NgranBands.BAND_70; + public static final int BAND_71 = android.hardware.radio.V1_5.NgranBands.BAND_71; + public static final int BAND_74 = android.hardware.radio.V1_5.NgranBands.BAND_74; + public static final int BAND_75 = android.hardware.radio.V1_5.NgranBands.BAND_75; + public static final int BAND_76 = android.hardware.radio.V1_5.NgranBands.BAND_76; + public static final int BAND_77 = android.hardware.radio.V1_5.NgranBands.BAND_77; + public static final int BAND_78 = android.hardware.radio.V1_5.NgranBands.BAND_78; + public static final int BAND_79 = android.hardware.radio.V1_5.NgranBands.BAND_79; + public static final int BAND_80 = android.hardware.radio.V1_5.NgranBands.BAND_80; + public static final int BAND_81 = android.hardware.radio.V1_5.NgranBands.BAND_81; + public static final int BAND_82 = android.hardware.radio.V1_5.NgranBands.BAND_82; + public static final int BAND_83 = android.hardware.radio.V1_5.NgranBands.BAND_83; + public static final int BAND_84 = android.hardware.radio.V1_5.NgranBands.BAND_84; + public static final int BAND_86 = android.hardware.radio.V1_5.NgranBands.BAND_86; + public static final int BAND_90 = android.hardware.radio.V1_5.NgranBands.BAND_90; /** FR2 bands */ - public static final int BAND_257 = 257; - public static final int BAND_258 = 258; - public static final int BAND_260 = 260; - public static final int BAND_261 = 261; + public static final int BAND_257 = android.hardware.radio.V1_5.NgranBands.BAND_257; + public static final int BAND_258 = android.hardware.radio.V1_5.NgranBands.BAND_258; + public static final int BAND_260 = android.hardware.radio.V1_5.NgranBands.BAND_260; + public static final int BAND_261 = android.hardware.radio.V1_5.NgranBands.BAND_261; /** * NR Bands diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index d504b381d1a1..fe75266011ed 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -74,17 +74,17 @@ public class ImsManager { "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR"; /** - * An extra key corresponding to a String value which contains the carrier specific title to be - * displayed as part of the message shown to the user when there is an error registering for - * WiFi calling. + * An extra key corresponding to a {@link CharSequence} value which contains the carrier + * specific title to be displayed as part of the message shown to the user when there is an + * error registering for WiFi calling. */ public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE"; /** - * An extra key corresponding to a String value which contains the carrier specific message to - * be displayed as part of the message shown to the user when there is an error registering for - * WiFi calling. + * An extra key corresponding to a {@link CharSequence} value which contains the carrier + * specific message to be displayed as part of the message shown to the user when there is an + * error registering for WiFi calling. */ public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE"; diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 08614b95d7b0..9b1baef1a70a 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -1411,6 +1411,7 @@ public class ServiceState implements Parcelable { DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo(); if (dsri != null) { dsri.setIsUsingCarrierAggregation(ca); + addNetworkRegistrationInfo(nri); } } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 27bbe68c6aa0..40def40c67a9 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -333,21 +333,6 @@ public class TelephonyManager { }; /** @hide */ - @IntDef(prefix = {"MODEM_COUNT_"}, - value = { - MODEM_COUNT_NO_MODEM, - MODEM_COUNT_SINGLE_MODEM, - MODEM_COUNT_DUAL_MODEM, - MODEM_COUNT_TRI_MODEM - }) - public @interface ModemCount {} - - public static final int MODEM_COUNT_NO_MODEM = 0; - public static final int MODEM_COUNT_SINGLE_MODEM = 1; - public static final int MODEM_COUNT_DUAL_MODEM = 2; - public static final int MODEM_COUNT_TRI_MODEM = 3; - - /** @hide */ @UnsupportedAppUsage public TelephonyManager(Context context) { this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); @@ -465,22 +450,22 @@ public class TelephonyManager { * Returns 2 for Dual standby mode (Dual SIM functionality). * Returns 3 for Tri standby mode (Tri SIM functionality). */ - public @ModemCount int getActiveModemCount() { + public int getActiveModemCount() { int modemCount = 1; switch (getMultiSimConfiguration()) { case UNKNOWN: - modemCount = MODEM_COUNT_SINGLE_MODEM; + modemCount = 1; // check for voice and data support, 0 if not supported if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) { - modemCount = MODEM_COUNT_NO_MODEM; + modemCount = 0; } break; case DSDS: case DSDA: - modemCount = MODEM_COUNT_DUAL_MODEM; + modemCount = 2; break; case TSTS: - modemCount = MODEM_COUNT_TRI_MODEM; + modemCount = 3; break; } return modemCount; @@ -493,7 +478,7 @@ public class TelephonyManager { * dual-SIM capable device operating in single SIM mode (only one logical modem is turned on), * {@link #getActiveModemCount} returns 1 while this API returns 2. */ - public @ModemCount int getSupportedModemCount() { + public int getSupportedModemCount() { return TelephonyProperties.max_active_modems().orElse(getActiveModemCount()); } @@ -12669,7 +12654,8 @@ public class TelephonyManager { * {@hide} */ @SystemApi - public boolean isCurrentSimOperator(@NonNull String mccmnc, @MvnoType int mvnoType, + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean matchesCurrentSimOperator(@NonNull String mccmnc, @MvnoType int mvnoType, @Nullable String mvnoMatchData) { try { if (!mccmnc.equals(getSimOperator())) { diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index aa356f1c35d2..66c4b1488b83 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -53,8 +53,7 @@ AtomDecl::AtomDecl(const AtomDecl &that) uidField(that.uidField), whitelisted(that.whitelisted), binaryFields(that.binaryFields), - hasModule(that.hasModule), - moduleName(that.moduleName) {} + moduleNames(that.moduleNames) {} AtomDecl::AtomDecl(int c, const string& n, const string& m) :code(c), @@ -442,9 +441,9 @@ int collate_atoms(const Descriptor *descriptor, Atoms *atoms) { atomDecl.whitelisted = true; } - if (atomField->options().HasExtension(os::statsd::module)) { - atomDecl.hasModule = true; - atomDecl.moduleName = atomField->options().GetExtension(os::statsd::module); + for (int j = 0; j < atomField->options().ExtensionSize(os::statsd::module); ++j) { + const string moduleName = atomField->options().GetExtension(os::statsd::module, j); + atomDecl.moduleNames.insert(moduleName); } vector<java_type_t> signature; @@ -453,36 +452,15 @@ int collate_atoms(const Descriptor *descriptor, Atoms *atoms) { errorCount++; } - // Add the signature if does not already exist. - auto signature_to_modules_it = atoms->signatures_to_modules.find(signature); - if (signature_to_modules_it == atoms->signatures_to_modules.end()) { - set<string> modules; - if (atomDecl.hasModule) { - modules.insert(atomDecl.moduleName); - } - atoms->signatures_to_modules[signature] = modules; - } else { - if (atomDecl.hasModule) { - signature_to_modules_it->second.insert(atomDecl.moduleName); - } - } + atoms->signatures_to_modules[signature].insert( + atomDecl.moduleNames.begin(), atomDecl.moduleNames.end()); atoms->decls.insert(atomDecl); AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name()); vector<java_type_t> nonChainedSignature; if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) { - auto it = atoms->non_chained_signatures_to_modules.find(nonChainedSignature); - if (it == atoms->non_chained_signatures_to_modules.end()) { - set<string> modules_non_chained; - if (atomDecl.hasModule) { - modules_non_chained.insert(atomDecl.moduleName); - } - atoms->non_chained_signatures_to_modules[nonChainedSignature] = modules_non_chained; - } else { - if (atomDecl.hasModule) { - it->second.insert(atomDecl.moduleName); - } - } + atoms->non_chained_signatures_to_modules[nonChainedSignature].insert( + atomDecl.moduleNames.begin(), atomDecl.moduleNames.end()); atoms->non_chained_decls.insert(nonChainedAtomDecl); } diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index 65d8e3efee5b..ace85e06f12d 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -98,8 +98,7 @@ struct AtomDecl { vector<int> binaryFields; - bool hasModule = false; - string moduleName; + set<string> moduleNames; AtomDecl(); AtomDecl(const AtomDecl& that); diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp index 984c929f63bd..58f13a4c1934 100644 --- a/tools/stats_log_api_gen/atoms_info_writer.cpp +++ b/tools/stats_log_api_gen/atoms_info_writer.cpp @@ -58,19 +58,25 @@ static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) { } static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { - std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed", - "audio_state_changed", - "call_state_changed", - "phone_signal_strength_changed", - "mobile_bytes_transfer_by_fg_bg", - "mobile_bytes_transfer"}; + std::set<string> kTruncatingAtomNames = { + "mobile_radio_power_state_changed", + "audio_state_changed", + "call_state_changed", + "phone_signal_strength_changed", + "mobile_bytes_transfer_by_fg_bg", + "mobile_bytes_transfer" + }; fprintf(out, "const std::set<int> " "AtomsInfo::kTruncatingTimestampAtomBlackList = {\n"); - for (set<string>::const_iterator blacklistedAtom = kTruncatingAtomNames.begin(); - blacklistedAtom != kTruncatingAtomNames.end(); blacklistedAtom++) { - fprintf(out, " %s,\n", make_constant_name(*blacklistedAtom).c_str()); + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); + atom != atoms.decls.end(); atom++) { + if (kTruncatingAtomNames.find(atom->name) != kTruncatingAtomNames.end()) { + const string constant = make_constant_name(atom->name); + fprintf(out, " %d, // %s\n", atom->code, constant.c_str()); + } } + fprintf(out, "};\n"); fprintf(out, "\n"); @@ -81,8 +87,8 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { for (vector<AtomField>::const_iterator field = atom->fields.begin(); field != atom->fields.end(); field++) { if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { - string constant = make_constant_name(atom->name); - fprintf(out, " %s,\n", constant.c_str()); + const string constant = make_constant_name(atom->name); + fprintf(out, " %d, // %s\n", atom->code, constant.c_str()); break; } } @@ -96,8 +102,8 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); atom++) { if (atom->whitelisted) { - string constant = make_constant_name(atom->name); - fprintf(out, " %s,\n", constant.c_str()); + const string constant = make_constant_name(atom->name); + fprintf(out, " %d, // %s\n", atom->code, constant.c_str()); } } @@ -105,7 +111,7 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { fprintf(out, "\n"); fprintf(out, "static std::map<int, int> getAtomUidField() {\n"); - fprintf(out, " std::map<int, int> uidField;\n"); + fprintf(out, " std::map<int, int> uidField;\n"); for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); atom++) { if (atom->uidField == 0) { @@ -115,8 +121,8 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { "\n // Adding uid field for atom " "(%d)%s\n", atom->code, atom->name.c_str()); - fprintf(out, " uidField[static_cast<int>(%s)] = %d;\n", - make_constant_name(atom->name).c_str(), atom->uidField); + fprintf(out, " uidField[%d /* %s */] = %d;\n", + atom->code, make_constant_name(atom->name).c_str(), atom->uidField); } fprintf(out, " return uidField;\n"); @@ -140,8 +146,8 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { "\n // Adding primary and exclusive fields for atom " "(%d)%s\n", atom->code, atom->name.c_str()); - fprintf(out, " opt = &(options[static_cast<int>(%s)]);\n", - make_constant_name(atom->name).c_str()); + fprintf(out, " opt = &(options[%d /* %s */]);\n", + atom->code, make_constant_name(atom->name).c_str()); fprintf(out, " opt->primaryFields.reserve(%lu);\n", atom->primaryFields.size()); for (const auto& field : atom->primaryFields) { fprintf(out, " opt->primaryFields.push_back(%d);\n", field); @@ -185,8 +191,8 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { atom->code, atom->name.c_str()); for (const auto& field : atom->binaryFields) { - fprintf(out, " options[static_cast<int>(%s)].push_back(%d);\n", - make_constant_name(atom->name).c_str(), field); + fprintf(out, " options[%d /* %s */].push_back(%d);\n", + atom->code, make_constant_name(atom->name).c_str(), field); } } @@ -222,12 +228,11 @@ int write_atoms_info_header(FILE* out, const Atoms &atoms, const string& namespa } int write_atoms_info_cpp(FILE *out, const Atoms &atoms, const string& namespaceStr, - const string& importHeader, const string& statslogHeader) { + const string& importHeader) { // Print prelude fprintf(out, "// This file is autogenerated\n"); fprintf(out, "\n"); fprintf(out, "#include <%s>\n", importHeader.c_str()); - fprintf(out, "#include <%s>\n", statslogHeader.c_str()); fprintf(out, "\n"); write_namespace(out, namespaceStr); diff --git a/tools/stats_log_api_gen/atoms_info_writer.h b/tools/stats_log_api_gen/atoms_info_writer.h index 12ac862871ef..d04e65a1b060 100644 --- a/tools/stats_log_api_gen/atoms_info_writer.h +++ b/tools/stats_log_api_gen/atoms_info_writer.h @@ -27,7 +27,7 @@ namespace stats_log_api_gen { using namespace std; int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr, - const string& importHeader, const string& statslogHeader); + const string& importHeader); int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr); diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 120c2a2caada..e9723a114e88 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -212,7 +212,7 @@ run(int argc, char const*const* argv) return 1; } errorCount = android::stats_log_api_gen::write_atoms_info_cpp( - out, atoms, cppNamespace, atomsInfoCppHeaderImport, cppHeaderImport); + out, atoms, cppNamespace, atomsInfoCppHeaderImport); fclose(out); } diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto index eab622d77ef7..f6c89c22ee65 100644 --- a/tools/stats_log_api_gen/test.proto +++ b/tools/stats_log_api_gen/test.proto @@ -213,6 +213,10 @@ message ModuleTwoAtom { optional int32 field = 1; } +message ModuleOneAndTwoAtom { + optional int32 field = 1; +} + message NoModuleAtom { optional string field = 1; } @@ -221,6 +225,9 @@ message ModuleAtoms { oneof event { ModuleOneAtom module_one_atom = 1 [(android.os.statsd.module) = "module1"]; ModuleTwoAtom module_two_atom = 2 [(android.os.statsd.module) = "module2"]; - NoModuleAtom no_module_atom = 3; + ModuleOneAndTwoAtom module_one_and_two_atom = 3 [ + (android.os.statsd.module) = "module1", (android.os.statsd.module) = "module2" + ]; + NoModuleAtom no_module_atom = 4; } } diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp index a972e2342cad..73abaef1d91b 100644 --- a/tools/stats_log_api_gen/test_collation.cpp +++ b/tools/stats_log_api_gen/test_collation.cpp @@ -248,23 +248,27 @@ TEST(CollationTest, PassOnLogFromModuleAtom) { Atoms atoms; int errorCount = collate_atoms(ModuleAtoms::descriptor(), &atoms); EXPECT_EQ(errorCount, 0); - EXPECT_EQ(atoms.decls.size(), 3ul); + EXPECT_EQ(atoms.decls.size(), 4ul); } TEST(CollationTest, RecognizeModuleAtom) { Atoms atoms; int errorCount = collate_atoms(ModuleAtoms::descriptor(), &atoms); EXPECT_EQ(errorCount, 0); - EXPECT_EQ(atoms.decls.size(), 3ul); + EXPECT_EQ(atoms.decls.size(), 4ul); for (const auto& atomDecl: atoms.decls) { if (atomDecl.code == 1) { - EXPECT_TRUE(atomDecl.hasModule); - EXPECT_EQ(atomDecl.moduleName, "module1"); + EXPECT_EQ(1ul, atomDecl.moduleNames.size()); + EXPECT_NE(atomDecl.moduleNames.end(), atomDecl.moduleNames.find("module1")); } else if (atomDecl.code == 2) { - EXPECT_TRUE(atomDecl.hasModule); - EXPECT_EQ(atomDecl.moduleName, "module2"); + EXPECT_EQ(1ul, atomDecl.moduleNames.size()); + EXPECT_NE(atomDecl.moduleNames.end(), atomDecl.moduleNames.find("module2")); + } else if (atomDecl.code == 3) { + EXPECT_EQ(2ul, atomDecl.moduleNames.size()); + EXPECT_NE(atomDecl.moduleNames.end(), atomDecl.moduleNames.find("module1")); + EXPECT_NE(atomDecl.moduleNames.end(), atomDecl.moduleNames.find("module2")); } else { - EXPECT_FALSE(atomDecl.hasModule); + EXPECT_TRUE(atomDecl.moduleNames.empty()); } } @@ -286,4 +290,4 @@ TEST(CollationTest, RecognizeModuleAtom) { } } // namespace stats_log_api_gen -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/tools/stats_log_api_gen/utils.cpp b/tools/stats_log_api_gen/utils.cpp index cd6914b4fc06..9dc4ff827380 100644 --- a/tools/stats_log_api_gen/utils.cpp +++ b/tools/stats_log_api_gen/utils.cpp @@ -102,7 +102,7 @@ bool atom_needed_for_module(const AtomDecl& atomDecl, const string& moduleName) if (moduleName == DEFAULT_MODULE_NAME) { return true; } - return atomDecl.hasModule && (moduleName == atomDecl.moduleName); + return atomDecl.moduleNames.find(moduleName) != atomDecl.moduleNames.end(); } bool signature_needed_for_module(const set<string>& modules, const string& moduleName) { diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java index 8ccf0076f0b0..de2f5d9a3fe4 100644 --- a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java +++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java @@ -242,6 +242,8 @@ public abstract class EasyConnectStatusCallback { * to scan to find the network, see the "DPP Connection Status Object" * section in the specification for the format, and Table E-4 in * IEEE Std 802.11-2016 - Global operating classes for more details. + * The sparse array key is the Global Operating class, and the value + * is an integer array of Wi-Fi channels. * @param operatingClassArray Array of bands the Enrollee supports as expressed as the Global * Operating Class, see Table E-4 in IEEE Std 802.11-2016 - Global * operating classes. |