Merge "Add createRequestInteractAcrossProfilesIntent API."
diff --git a/Android.bp b/Android.bp
index 0d9cbd8..8c85631 100644
--- a/Android.bp
+++ b/Android.bp
@@ -416,6 +416,13 @@
}
filegroup {
+ name: "graphicsstats_proto",
+ srcs: [
+ "libs/hwui/protos/graphicsstats.proto",
+ ],
+}
+
+filegroup {
name: "libvibrator_aidl",
srcs: [
"core/java/android/os/IExternalVibrationController.aidl",
@@ -487,7 +494,8 @@
// TODO(b/140299412): should be framework-wifi-stubs
"framework-wifi",
"ike-stubs",
- // TODO(jiyong): add more stubs for APEXes here
+ // TODO(b/147200698): should be the stub of framework-tethering
+ "framework-tethering",
],
sdk_version: "core_platform",
apex_available: ["//apex_available:platform"],
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index 3dc5a2c..1f30dda 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -26,9 +26,16 @@
installable: true,
sdk_version: "core_platform", // TODO(b/146218515) should be core_current
srcs: [":framework-appsearch-sources"],
+ hostdex: true, // for hiddenapi check
libs: [
"framework-minus-apex", // TODO(b/146218515) should be framework-system-stubs
],
+ visibility: [
+ "//frameworks/base/apex/appsearch:__subpackages__",
+ // TODO(b/146218515) remove this when framework is built with the stub of appsearch
+ "//frameworks/base",
+ ],
+ apex_available: ["com.android.appsearch"],
}
metalava_appsearch_docs_args =
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 6bd0086..18382a4 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -55,6 +55,13 @@
jarjar_rules: "jarjar_rules.txt",
plugins: ["java_api_finder"],
+
+ hostdex: true, // for hiddenapi check
+ visibility: ["//frameworks/av/apex:__subpackages__"],
+ apex_available: [
+ "com.android.media",
+ "test_com.android.media",
+ ],
}
filegroup {
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 5504f4e..dd17473 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -36,6 +36,11 @@
"//frameworks/base/apex/sdkextensions",
"//frameworks/base/apex/sdkextensions/testing",
],
+ hostdex: true, // for hiddenapi check
+ apex_available: [
+ "com.android.sdkext",
+ "test_com.android.sdkext",
+ ],
}
droidstubs {
diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
index 21b7767..99b9d39 100644
--- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
@@ -67,11 +67,4 @@
/** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
oneway void triggerUidSnapshot();
-
- /** Tells StatsCompanionService to tell statsd to register a puller for the given atom id */
- oneway void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
- in int[] additiveFields, IPullAtomCallback pullerCallback);
-
- /** Tells StatsCompanionService to tell statsd to unregister a puller for the given atom id */
- oneway void unregisterPullAtomCallback(int atomTag);
}
diff --git a/apex/statsd/aidl/android/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
index dec5634..4a259f5 100644
--- a/apex/statsd/aidl/android/os/IStatsManagerService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.app.PendingIntent;
+import android.os.IPullAtomCallback;
/**
* Binder interface to communicate with the Java-based statistics service helper.
@@ -125,4 +126,11 @@
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
*/
void removeConfiguration(in long configId, in String packageName);
-}
\ No newline at end of file
+
+ /** Tell StatsManagerService to register a puller for the given atom tag with statsd. */
+ oneway void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
+ in int[] additiveFields, IPullAtomCallback pullerCallback);
+
+ /** Tell StatsManagerService to unregister the pulller for the given atom tag from statsd. */
+ oneway void unregisterPullAtomCallback(int atomTag);
+}
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index a2b0577..0b46645a 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -37,7 +37,16 @@
// TODO(b/146230220): Use framework-system-stubs instead.
"android_system_stubs_current",
],
- // TODO:(b/146210774): Add apex_available field.
+ hostdex: true, // for hiddenapi check
+ visibility: [
+ "//frameworks/base/apex/statsd:__subpackages__",
+ //TODO(b/146167933) remove this when framework is built with framework-statsd-stubs
+ "//frameworks/base",
+ ],
+ apex_available: [
+ "com.android.os.statsd",
+ "test_com.android.os.statsd",
+ ],
}
droidstubs {
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index d57afee..a908178 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -75,7 +75,6 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.IPullAtomCallback;
import android.os.IStatsCompanionService;
import android.os.IStatsd;
import android.os.IStoraged;
@@ -263,71 +262,6 @@
private StatsManagerService mStatsManagerService;
- private static final class PullerKey {
- private final int mUid;
- private final int mAtomTag;
-
- PullerKey(int uid, int atom) {
- mUid = uid;
- mAtomTag = atom;
- }
-
- public int getUid() {
- return mUid;
- }
-
- public int getAtom() {
- return mAtomTag;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mUid, mAtomTag);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof PullerKey) {
- PullerKey other = (PullerKey) obj;
- return this.mUid == other.getUid() && this.mAtomTag == other.getAtom();
- }
- return false;
- }
- }
-
- private static final class PullerValue {
- private final long mCoolDownNs;
- private final long mTimeoutNs;
- private int[] mAdditiveFields;
- private IPullAtomCallback mCallback;
-
- PullerValue(long coolDownNs, long timeoutNs, int[] additiveFields,
- IPullAtomCallback callback) {
- mCoolDownNs = coolDownNs;
- mTimeoutNs = timeoutNs;
- mAdditiveFields = additiveFields;
- mCallback = callback;
- }
-
- public long getCoolDownNs() {
- return mCoolDownNs;
- }
-
- public long getTimeoutNs() {
- return mTimeoutNs;
- }
-
- public int[] getAdditiveFields() {
- return mAdditiveFields;
- }
-
- public IPullAtomCallback getCallback() {
- return mCallback;
- }
- }
-
- private final HashMap<PullerKey, PullerValue> mPullers = new HashMap<>();
-
private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
private WifiManager mWifiManager = null;
@@ -2634,57 +2568,6 @@
}
}
- @Override
- public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
- int[] additiveFields, IPullAtomCallback pullerCallback) {
- synchronized (sStatsdLock) {
- // Always cache the puller in SCS.
- // If statsd is down, we will register it when it comes back up.
- int callingUid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- PullerKey key = new PullerKey(callingUid, atomTag);
- PullerValue val = new PullerValue(
- coolDownNs, timeoutNs, additiveFields, pullerCallback);
- mPullers.put(key, val);
-
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd for registering puller for atom " + atomTag);
- return;
- }
- try {
- sStatsd.registerPullAtomCallback(
- callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
- @Override
- public void unregisterPullAtomCallback(int atomTag) {
- synchronized (sStatsdLock) {
- // Always remove the puller in SCS.
- // If statsd is down, we will not register it when it comes back up.
- int callingUid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- PullerKey key = new PullerKey(callingUid, atomTag);
- mPullers.remove(key);
-
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd for registering puller for atom " + atomTag);
- return;
- }
- try {
- sStatsd.unregisterPullAtomCallback(callingUid, atomTag);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
// Statsd related code
@@ -2763,8 +2646,6 @@
// Pull the latest state of UID->app name, version mapping when
// statsd starts.
informAllUidsLocked(mContext);
- // Register all pullers. If SCS has just started, this should be empty.
- registerAllPullersLocked();
} finally {
restoreCallingIdentity(token);
}
@@ -2776,17 +2657,6 @@
}
}
- @GuardedBy("sStatsdLock")
- private void registerAllPullersLocked() throws RemoteException {
- // TODO: pass in one call, using a file descriptor (similar to uidmap).
- for (Map.Entry<PullerKey, PullerValue> entry : mPullers.entrySet()) {
- PullerKey key = entry.getKey();
- PullerValue val = entry.getValue();
- sStatsd.registerPullAtomCallback(key.getUid(), key.getAtom(), val.getCoolDownNs(),
- val.getTimeoutNs(), val.getAdditiveFields(), val.getCallback());
- }
- }
-
private class StatsdDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
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 b27d0f7..04d8b00 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -24,6 +24,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.os.Binder;
+import android.os.IPullAtomCallback;
import android.os.IStatsManagerService;
import android.os.IStatsd;
import android.os.Process;
@@ -60,8 +61,7 @@
@GuardedBy("mLock")
private ArrayMap<ConfigKey, PendingIntentRef> mDataFetchPirMap = new ArrayMap<>();
@GuardedBy("mLock")
- private ArrayMap<Integer, PendingIntentRef> mActiveConfigsPirMap =
- new ArrayMap<>();
+ private ArrayMap<Integer, PendingIntentRef> mActiveConfigsPirMap = new ArrayMap<>();
@GuardedBy("mLock")
private ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> mBroadcastSubscriberPirMap =
new ArrayMap<>();
@@ -72,8 +72,8 @@
}
private static class ConfigKey {
- private int mUid;
- private long mConfigId;
+ private final int mUid;
+ private final long mConfigId;
ConfigKey(int uid, long configId) {
mUid = uid;
@@ -103,6 +103,126 @@
}
}
+ private static class PullerKey {
+ private final int mUid;
+ private final int mAtomTag;
+
+ PullerKey(int uid, int atom) {
+ mUid = uid;
+ mAtomTag = atom;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public int getAtom() {
+ return mAtomTag;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUid, mAtomTag);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof PullerKey) {
+ PullerKey other = (PullerKey) obj;
+ return this.mUid == other.getUid() && this.mAtomTag == other.getAtom();
+ }
+ return false;
+ }
+ }
+
+ private static class PullerValue {
+ private final long mCoolDownNs;
+ private final long mTimeoutNs;
+ private final int[] mAdditiveFields;
+ private final IPullAtomCallback mCallback;
+
+ PullerValue(long coolDownNs, long timeoutNs, int[] additiveFields,
+ IPullAtomCallback callback) {
+ mCoolDownNs = coolDownNs;
+ mTimeoutNs = timeoutNs;
+ mAdditiveFields = additiveFields;
+ mCallback = callback;
+ }
+
+ public long getCoolDownNs() {
+ return mCoolDownNs;
+ }
+
+ public long getTimeoutNs() {
+ return mTimeoutNs;
+ }
+
+ public int[] getAdditiveFields() {
+ return mAdditiveFields;
+ }
+
+ public IPullAtomCallback getCallback() {
+ return mCallback;
+ }
+ }
+
+ private final ArrayMap<PullerKey, PullerValue> mPullers = new ArrayMap<>();
+
+ @Override
+ public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
+ int[] additiveFields, IPullAtomCallback pullerCallback) {
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ PullerKey key = new PullerKey(callingUid, atomTag);
+ PullerValue val = new PullerValue(coolDownNs, timeoutNs, additiveFields, pullerCallback);
+
+ // Always cache the puller in StatsManagerService. If statsd is down, we will register the
+ // puller when statsd comes back up.
+ synchronized (mLock) {
+ mPullers.put(key, val);
+ }
+
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd == null) {
+ return;
+ }
+
+ try {
+ statsd.registerPullAtomCallback(
+ callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unregisterPullAtomCallback(int atomTag) {
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ PullerKey key = new PullerKey(callingUid, atomTag);
+
+ // Always remove the puller from StatsManagerService even if statsd is down. When statsd
+ // comes back up, we will not re-register the removed puller.
+ synchronized (mLock) {
+ mPullers.remove(key);
+ }
+
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd == null) {
+ return;
+ }
+
+ try {
+ statsd.unregisterPullAtomCallback(callingUid, atomTag);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to access statsd to unregister puller for atom " + atomTag);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
public void setDataFetchOperation(long configId, PendingIntent pendingIntent,
String packageName) {
@@ -441,46 +561,85 @@
if (statsd == null) {
return;
}
- // Since we do not want to make an IPC with the a lock held, we first create local deep
- // copies of the data with the lock held before iterating through the maps.
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ registerAllPullers(statsd);
+ registerAllDataFetchOperations(statsd);
+ registerAllActiveConfigsChangedOperations(statsd);
+ registerAllBroadcastSubscribers(statsd);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "StatsManager failed to (re-)register data with statsd");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Pre-condition: the Binder calling identity has already been cleared
+ private void registerAllPullers(IStatsd statsd) throws RemoteException {
+ // Since we do not want to make an IPC with the lock held, we first create a copy of the
+ // data with the lock held before iterating through the map.
+ ArrayMap<PullerKey, PullerValue> pullersCopy;
+ synchronized (mLock) {
+ pullersCopy = new ArrayMap<>(mPullers);
+ }
+
+ 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());
+ }
+ }
+
+ // Pre-condition: the Binder calling identity has already been cleared
+ private void registerAllDataFetchOperations(IStatsd statsd) throws RemoteException {
+ // Since we do not want to make an IPC with the lock held, we first create a copy of the
+ // data with the lock held before iterating through the map.
ArrayMap<ConfigKey, PendingIntentRef> dataFetchCopy;
- ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy;
- ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy;
synchronized (mLock) {
dataFetchCopy = new ArrayMap<>(mDataFetchPirMap);
- activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap);
- broadcastSubscriberCopy = new ArrayMap<>();
- for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry
- : mBroadcastSubscriberPirMap.entrySet()) {
- broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap<>(entry.getValue()));
- }
}
+
for (Map.Entry<ConfigKey, PendingIntentRef> entry : dataFetchCopy.entrySet()) {
ConfigKey key = entry.getKey();
- try {
- statsd.setDataFetchOperation(key.getConfigId(), entry.getValue(), key.getUid());
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setDataFetchOperation from pirMap");
+ statsd.setDataFetchOperation(key.getConfigId(), entry.getValue(), key.getUid());
+ }
+ }
+
+ // Pre-condition: the Binder calling identity has already been cleared
+ private void registerAllActiveConfigsChangedOperations(IStatsd statsd) throws RemoteException {
+ // Since we do not want to make an IPC with the lock held, we first create a copy of the
+ // data with the lock held before iterating through the map.
+ ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy;
+ synchronized (mLock) {
+ activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap);
+ }
+
+ for (Map.Entry<Integer, PendingIntentRef> entry : activeConfigsChangedCopy.entrySet()) {
+ statsd.setActiveConfigsChangedOperation(entry.getValue(), entry.getKey());
+ }
+ }
+
+ // Pre-condition: the Binder calling identity has already been cleared
+ private void registerAllBroadcastSubscribers(IStatsd statsd) throws RemoteException {
+ // Since we do not want to make an IPC with the lock held, we first create a deep copy of
+ // the data with the lock held before iterating through the map.
+ ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy =
+ new ArrayMap<>();
+ synchronized (mLock) {
+ for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry :
+ mBroadcastSubscriberPirMap.entrySet()) {
+ broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap(entry.getValue()));
}
}
- for (Map.Entry<Integer, PendingIntentRef> entry
- : activeConfigsChangedCopy.entrySet()) {
- try {
- statsd.setActiveConfigsChangedOperation(entry.getValue(), entry.getKey());
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setActiveConfigsChangedOperation from pirMap");
- }
- }
- for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry
- : broadcastSubscriberCopy.entrySet()) {
+
+ for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry :
+ mBroadcastSubscriberPirMap.entrySet()) {
+ ConfigKey configKey = entry.getKey();
for (Map.Entry<Long, PendingIntentRef> subscriberEntry : entry.getValue().entrySet()) {
- ConfigKey configKey = entry.getKey();
- try {
- statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(),
- subscriberEntry.getValue(), configKey.getUid());
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setBroadcastSubscriber from pirMap");
- }
+ statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(),
+ subscriberEntry.getValue(), configKey.getUid());
}
}
}
diff --git a/api/current.txt b/api/current.txt
index ad7881c..b0d5fbf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5837,6 +5837,7 @@
method public void enableLights(boolean);
method public void enableVibration(boolean);
method public android.media.AudioAttributes getAudioAttributes();
+ method @Nullable public String getConversationId();
method public String getDescription();
method public String getGroup();
method public String getId();
@@ -5844,12 +5845,14 @@
method public int getLightColor();
method public int getLockscreenVisibility();
method public CharSequence getName();
+ method @Nullable public String getParentChannelId();
method public android.net.Uri getSound();
method public long[] getVibrationPattern();
method public boolean hasUserSetImportance();
method public boolean hasUserSetSound();
method public void setAllowBubbles(boolean);
method public void setBypassDnd(boolean);
+ method public void setConversationId(@Nullable String, @Nullable String);
method public void setDescription(String);
method public void setGroup(String);
method public void setImportance(int);
@@ -5862,6 +5865,7 @@
method public boolean shouldShowLights();
method public boolean shouldVibrate();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
field @NonNull public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR;
field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
}
@@ -5903,6 +5907,7 @@
method public final int getCurrentInterruptionFilter();
method public int getImportance();
method public android.app.NotificationChannel getNotificationChannel(String);
+ method @Nullable public android.app.NotificationChannel getNotificationChannel(@NonNull String, @NonNull String);
method public android.app.NotificationChannelGroup getNotificationChannelGroup(String);
method public java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups();
method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
@@ -9823,6 +9828,7 @@
method public boolean bindIsolatedService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
method public abstract boolean bindService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int);
method public boolean bindService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
+ method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
method @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") public abstract int checkCallingOrSelfPermission(@NonNull String);
method @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") public abstract int checkCallingOrSelfUriPermission(android.net.Uri, int);
method @CheckResult(suggest="#enforceCallingPermission(String,String)") public abstract int checkCallingPermission(@NonNull String);
@@ -35832,6 +35838,7 @@
field public static final int THREAD_PRIORITY_URGENT_AUDIO = -19; // 0xffffffed
field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8
field public static final int THREAD_PRIORITY_VIDEO = -10; // 0xfffffff6
+ field public static final int WIFI_UID = 1010; // 0x3f2
}
public abstract class ProxyFileDescriptorCallback {
@@ -42675,9 +42682,13 @@
method public android.content.Intent createEnrollIntent();
method public android.content.Intent createReEnrollIntent();
method public android.content.Intent createUnEnrollIntent();
+ method public int getParameter(int);
method public int getSupportedRecognitionModes();
+ method @Nullable public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
+ method public int setParameter(int, int);
method public boolean startRecognition(int);
method public boolean stopRecognition();
+ field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
@@ -42702,6 +42713,11 @@
method @Nullable public byte[] getTriggerAudio();
}
+ public static final class AlwaysOnHotwordDetector.ModelParamRange {
+ method public int end();
+ method public int start();
+ }
+
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
@@ -51608,9 +51624,10 @@
method public boolean dispatchUnhandledMove(android.view.View, int);
method protected void dispatchVisibilityChanged(@NonNull android.view.View, int);
method public void dispatchWindowFocusChanged(boolean);
- method public void dispatchWindowInsetsAnimationFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+ method public void dispatchWindowInsetsAnimationFinish(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+ method public void dispatchWindowInsetsAnimationPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
method @NonNull public android.view.WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull android.view.WindowInsets);
- method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
+ method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
method public void dispatchWindowSystemUiVisiblityChanged(int);
method public void dispatchWindowVisibilityChanged(int);
method @CallSuper public void draw(android.graphics.Canvas);
@@ -53290,9 +53307,10 @@
}
public interface WindowInsetsAnimationCallback {
- method public default void onFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+ method public default void onFinish(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+ method public default void onPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
method @NonNull public android.view.WindowInsets onProgress(@NonNull android.view.WindowInsets);
- method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
+ method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
}
public static final class WindowInsetsAnimationCallback.AnimationBounds {
diff --git a/api/system-current.txt b/api/system-current.txt
index 9dbdef8..5aff7c5 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -152,6 +152,7 @@
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
field public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
+ field public static final String RADIO_SCAN_WITHOUT_LOCATION = "android.permission.RADIO_SCAN_WITHOUT_LOCATION";
field public static final String READ_ACTIVE_EMERGENCY_SESSION = "android.permission.READ_ACTIVE_EMERGENCY_SESSION";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
@@ -1671,7 +1672,6 @@
}
public abstract class Context {
- method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
method public abstract android.content.Context createCredentialProtectedStorageContext();
method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -1780,6 +1780,7 @@
field @Deprecated public static final String EXTRA_SIM_STATE = "ss";
field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
+ field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000
field public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
field @Deprecated public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
field @Deprecated public static final String SIM_LOCKED_NETWORK = "NETWORK";
@@ -3545,7 +3546,6 @@
}
public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
- method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModelParamRange> CREATOR;
field public final int end;
@@ -3566,6 +3566,7 @@
field public final int powerConsumptionMw;
field public final int recognitionModes;
field public final boolean returnsTriggerInEvent;
+ field @NonNull public final String supportedModelArch;
field public final boolean supportsCaptureTransition;
field public final boolean supportsConcurrentCapture;
field @NonNull public final java.util.UUID uuid;
@@ -4311,7 +4312,7 @@
}
public static interface MediaSessionManager.OnMediaKeyEventDispatchedListener {
- method public default void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @NonNull android.media.session.MediaSession.Token);
+ method public default void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @Nullable android.media.session.MediaSession.Token);
}
public static interface MediaSessionManager.OnMediaKeyEventSessionChangedListener {
@@ -4367,9 +4368,9 @@
method public int getDetectionServiceOperationsTimeout();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModuleProperties getModuleProperties();
- method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getParameter(@NonNull java.util.UUID, int) throws java.lang.IllegalArgumentException, java.lang.UnsupportedOperationException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getParameter(@NonNull java.util.UUID, int);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModelParamRange queryParameter(@Nullable java.util.UUID, int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int setParameter(@Nullable java.util.UUID, int, int) throws java.lang.IllegalArgumentException, java.lang.UnsupportedOperationException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int setParameter(@Nullable java.util.UUID, int, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model);
}
@@ -5836,8 +5837,8 @@
method public int getMaxNumberOfClients();
method @Nullable public String getPassphrase();
method public int getSecurityType();
+ method public int getShutdownTimeoutMillis();
method @Nullable public String getSsid();
- method @Nullable public String getWpa2Passphrase();
method public boolean isHiddenSsid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int BAND_2GHZ = 1; // 0x1
@@ -5861,8 +5862,8 @@
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
- method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String);
}
public final class SoftApInfo implements android.os.Parcelable {
@@ -6025,6 +6026,7 @@
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(@NonNull java.util.List<android.net.wifi.ScanResult>);
method public boolean isApMacRandomizationSupported();
method public boolean isConnectedMacRandomizationSupported();
method @Deprecated public boolean isDeviceToDeviceRttSupported();
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 19b9709..f6680f3 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -336,7 +336,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10068
+ // Next: 10069
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -405,6 +405,7 @@
VmsClientStats vms_client_stats = 10065;
NotificationRemoteViews notification_remote_views = 10066;
DangerousPermissionStateSampled dangerous_permission_state_sampled = 10067;
+ GraphicsStats graphics_stats = 10068;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -7553,3 +7554,69 @@
optional int32 permission_flags = 4;
}
+/**
+ * HWUI renders pipeline type: GL (0) or Vulkan (1).
+ */
+enum PipelineType {
+ GL = 0;
+ VULKAN = 1;
+}
+
+/**
+ * HWUI stats for a given app.
+ */
+message GraphicsStats {
+ // The package name of the app
+ optional string package_name = 1;
+
+ // The version code of the app
+ optional int64 version_code = 2;
+
+ // The start & end timestamps in UTC as
+ // milliseconds since January 1, 1970
+ // Compatible with java.util.Date#setTime()
+ optional int64 stats_start = 3;
+
+ optional int64 stats_end = 4;
+
+ // HWUI renders pipeline type: GL or Vulkan.
+ optional PipelineType pipeline = 5;
+
+ // Distinct frame count.
+ optional int32 total_frames = 6;
+
+ // Number of "missed vsync" events.
+ optional int32 missed_vsync_count = 7;
+
+ // Number of frames in triple-buffering scenario (high input latency)
+ optional int32 high_input_latency_count = 8;
+
+ // Number of "slow UI thread" events.
+ optional int32 slow_ui_thread_count = 9;
+
+ // Number of "slow bitmap upload" events.
+ optional int32 slow_bitmap_upload_count = 10;
+
+ // Number of "slow draw" events.
+ optional int32 slow_draw_count = 11;
+
+ // Number of frames that missed their deadline (aka, visibly janked)
+ optional int32 missed_deadline_count = 12;
+
+ // The frame time histogram for the package
+ optional FrameTimingHistogram cpu_histogram = 13
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+
+ // The gpu frame time histogram for the package
+ optional FrameTimingHistogram gpu_histogram = 14
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+
+ // UI mainline module version.
+ optional int64 version_ui_module = 15;
+
+ // If true, these are HWUI stats for up to a 24h period for a given app from today.
+ // If false, these are HWUI stats for a 24h period for a given app from the last complete
+ // day (yesterday). Stats from yesterday stay constant, while stats from today may change as
+ // more apps are running / rendering.
+ optional bool is_today = 16;
+}
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 4d0acb3..4aaf727 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -22519,8 +22519,6 @@
HSPLcom/android/internal/telephony/PhoneFactory;->makeDefaultPhones(Landroid/content/Context;)V
HSPLcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;-><init>(Ljava/lang/String;I)V
HSPLcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;->values()[Lcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;
-HSPLcom/android/internal/telephony/PhoneStateIntentReceiver;-><init>(Landroid/content/Context;Landroid/os/Handler;)V
-HSPLcom/android/internal/telephony/PhoneStateIntentReceiver;->notifyServiceState(I)V
HSPLcom/android/internal/telephony/PhoneSubInfoController;->callPhoneMethodWithPermissionCheck(ILjava/lang/String;Ljava/lang/String;Lcom/android/internal/telephony/PhoneSubInfoController$CallPhoneMethodHelper;Lcom/android/internal/telephony/PhoneSubInfoController$PermissionCheckHelper;)Ljava/lang/Object;
HSPLcom/android/internal/telephony/PhoneSubInfoController;->getCarrierInfoForImsiEncryption(IILjava/lang/String;)Landroid/telephony/ImsiEncryptionInfo;
HSPLcom/android/internal/telephony/PhoneSubInfoController;->getGroupIdLevel1ForSubscriber(ILjava/lang/String;)Ljava/lang/String;
@@ -37729,7 +37727,6 @@
Lcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;
Lcom/android/internal/telephony/PhoneInternalInterface;
Lcom/android/internal/telephony/PhoneNotifier;
-Lcom/android/internal/telephony/PhoneStateIntentReceiver;
Lcom/android/internal/telephony/PhoneSubInfoController$CallPhoneMethodHelper;
Lcom/android/internal/telephony/PhoneSubInfoController$PermissionCheckHelper;
Lcom/android/internal/telephony/PhoneSubInfoController;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 97f009c..e53c74b 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -4841,7 +4841,6 @@
com.android.internal.telephony.PhoneFactory
com.android.internal.telephony.PhoneInternalInterface
com.android.internal.telephony.PhoneNotifier
-com.android.internal.telephony.PhoneStateIntentReceiver
com.android.internal.telephony.PhoneSubInfoController$CallPhoneMethodHelper
com.android.internal.telephony.PhoneSubInfoController$PermissionCheckHelper
com.android.internal.telephony.PhoneSubInfoController
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 032e824..4f3e8ec 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -45,8 +45,19 @@
// Access modes for handleIncomingUser.
public static final int ALLOW_NON_FULL = 0;
+ /**
+ * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+ * if in the same profile group.
+ * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
+ */
public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
public static final int ALLOW_FULL_ONLY = 2;
+ /**
+ * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
+ * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} if in the same profile group.
+ * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
+ */
+ public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3;
/**
* Verify that calling app has access to the given provider.
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 86f52af..fcdb7cc 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -94,8 +94,11 @@
void updateNotificationChannelGroupForPackage(String pkg, int uid, in NotificationChannelGroup group);
void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
NotificationChannel getNotificationChannel(String callingPkg, int userId, String pkg, String channelId);
+ NotificationChannel getConversationNotificationChannel(String callingPkg, int userId, String pkg, String channelId, String conversationId);
+ void createConversationNotificationChannelForPackage(String pkg, int uid, in NotificationChannel parentChannel, String conversationId);
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, String channelId);
+ void deleteConversationNotificationChannels(String pkg, int uid, String conversationId);
ParceledListSlice getNotificationChannels(String callingPkg, String targetPkg, int userId);
ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 3eee1ae..a33c2c1 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -23,6 +23,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Parcel;
@@ -57,6 +58,22 @@
public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
/**
+ * The formatter used by the system to create an id for notification
+ * channels when it automatically creates conversation channels on behalf of an app. The format
+ * string takes two arguments, in this order: the
+ * {@link #getId()} of the original notification channel, and the
+ * {@link ShortcutInfo#getId() id} of the conversation.
+ */
+ public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
+
+ /**
+ * TODO: STOPSHIP remove
+ * Conversation id to use for apps that aren't providing them yet.
+ * @hide
+ */
+ public static final String PLACEHOLDER_CONVERSATION_ID = "placeholder_id";
+
+ /**
* The maximum length for text fields in a NotificationChannel. Fields will be truncated at this
* limit.
*/
@@ -85,6 +102,8 @@
private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
private static final String ATT_ALLOW_BUBBLE = "can_bubble";
private static final String ATT_ORIG_IMP = "orig_imp";
+ private static final String ATT_PARENT_CHANNEL = "parent";
+ private static final String ATT_CONVERSATION_ID = "conv_id";
private static final String DELIMITER = ",";
/**
@@ -147,7 +166,7 @@
private static final boolean DEFAULT_ALLOW_BUBBLE = true;
@UnsupportedAppUsage
- private final String mId;
+ private String mId;
private String mName;
private String mDesc;
private int mImportance = DEFAULT_IMPORTANCE;
@@ -172,6 +191,8 @@
private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE;
private boolean mImportanceLockedByOEM;
private boolean mImportanceLockedDefaultApp;
+ private String mParentId = null;
+ private String mConversationId = null;
/**
* Creates a notification channel.
@@ -236,6 +257,8 @@
mAllowBubbles = in.readBoolean();
mImportanceLockedByOEM = in.readBoolean();
mOriginalImportance = in.readInt();
+ mParentId = in.readString();
+ mConversationId = in.readString();
}
@Override
@@ -291,6 +314,8 @@
dest.writeBoolean(mAllowBubbles);
dest.writeBoolean(mImportanceLockedByOEM);
dest.writeInt(mOriginalImportance);
+ dest.writeString(mParentId);
+ dest.writeString(mConversationId);
}
/**
@@ -363,6 +388,13 @@
// Modifiable by apps on channel creation.
/**
+ * @hide
+ */
+ public void setId(String id) {
+ mId = id;
+ }
+
+ /**
* Sets what group this channel belongs to.
*
* Group information is only used for presentation, not for behavior.
@@ -502,6 +534,23 @@
}
/**
+ * Sets this channel as being person-centric. Different settings and functionality may be
+ * exposed for people-centric channels.
+ *
+ * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of
+ * this type would be posted to in absence of a specific conversation id.
+ * For example, if this channel represents 'Messages from Person A', the
+ * parent channel would be 'Messages.'
+ * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this
+ * channel's conversation.
+ */
+ public void setConversationId(@Nullable String parentChannelId,
+ @Nullable String conversationId) {
+ mParentId = parentChannelId;
+ mConversationId = conversationId;
+ }
+
+ /**
* Returns the id of this channel.
*/
public String getId() {
@@ -622,6 +671,22 @@
}
/**
+ * Returns the {@link #getId() id} of the parent notification channel to this channel, if it's
+ * a conversation related channel. See {@link #setConversationId(String, String)}.
+ */
+ public @Nullable String getParentChannelId() {
+ return mParentId;
+ }
+
+ /**
+ * Returns the {@link ShortcutInfo#getId() id} of the conversation backing this channel, if it's
+ * associated with a conversation. See {@link #setConversationId(String, String)}.
+ */
+ public @Nullable String getConversationId() {
+ return mConversationId;
+ }
+
+ /**
* @hide
*/
@SystemApi
@@ -761,6 +826,8 @@
setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE));
+ setConversationId(parser.getAttributeValue(ATT_PARENT_CHANNEL, null),
+ parser.getAttributeValue(ATT_CONVERSATION_ID, null));
}
@Nullable
@@ -885,6 +952,12 @@
if (getOriginalImportance() != DEFAULT_IMPORTANCE) {
out.attribute(null, ATT_ORIG_IMP, Integer.toString(getOriginalImportance()));
}
+ if (getParentChannelId() != null) {
+ out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId());
+ }
+ if (getConversationId() != null) {
+ out.attribute(null, ATT_CONVERSATION_ID, getConversationId());
+ }
// mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
// truth and so aren't written to this xml file
@@ -1042,7 +1115,9 @@
&& Objects.equals(getAudioAttributes(), that.getAudioAttributes())
&& mImportanceLockedByOEM == that.mImportanceLockedByOEM
&& mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
- && mOriginalImportance == that.mOriginalImportance;
+ && mOriginalImportance == that.mOriginalImportance
+ && Objects.equals(getParentChannelId(), that.getParentChannelId())
+ && Objects.equals(getConversationId(), that.getConversationId());
}
@Override
@@ -1052,7 +1127,8 @@
getUserLockedFields(),
isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
- mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance);
+ mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
+ mParentId, mConversationId);
result = 31 * result + Arrays.hashCode(mVibration);
return result;
}
@@ -1063,26 +1139,7 @@
String output = "NotificationChannel{"
+ "mId='" + mId + '\''
+ ", mName=" + redactedName
- + ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
- + ", mImportance=" + mImportance
- + ", mBypassDnd=" + mBypassDnd
- + ", mLockscreenVisibility=" + mLockscreenVisibility
- + ", mSound=" + mSound
- + ", mLights=" + mLights
- + ", mLightColor=" + mLightColor
- + ", mVibration=" + Arrays.toString(mVibration)
- + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
- + ", mFgServiceShown=" + mFgServiceShown
- + ", mVibrationEnabled=" + mVibrationEnabled
- + ", mShowBadge=" + mShowBadge
- + ", mDeleted=" + mDeleted
- + ", mGroup='" + mGroup + '\''
- + ", mAudioAttributes=" + mAudioAttributes
- + ", mBlockableSystem=" + mBlockableSystem
- + ", mAllowBubbles=" + mAllowBubbles
- + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
- + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
- + ", mOriginalImp=" + mOriginalImportance
+ + getFieldsString()
+ '}';
pw.println(prefix + output);
}
@@ -1092,7 +1149,12 @@
return "NotificationChannel{"
+ "mId='" + mId + '\''
+ ", mName=" + mName
- + ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
+ + getFieldsString()
+ + '}';
+ }
+
+ private String getFieldsString() {
+ return ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
+ ", mImportance=" + mImportance
+ ", mBypassDnd=" + mBypassDnd
+ ", mLockscreenVisibility=" + mLockscreenVisibility
@@ -1112,7 +1174,8 @@
+ ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
+ ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
+ ", mOriginalImp=" + mOriginalImportance
- + '}';
+ + ", mParent=" + mParentId
+ + ", mConversationId" + mConversationId;
}
/** @hide */
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index fdbb8bb..61c1098 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -843,6 +843,25 @@
}
/**
+ * Returns the notification channel settings for a given channel and conversation id.
+ *
+ * <p>The channel must belong to your package, or to a package you are an approved notification
+ * delegate for (see {@link #canNotifyAsPackage(String)}), or it will not be returned. To query
+ * a channel as a notification delegate, call this method from a context created for that
+ * package (see {@link Context#createPackageContext(String, int)}).</p>
+ */
+ public @Nullable NotificationChannel getNotificationChannel(@NonNull String channelId,
+ @NonNull String conversationId) {
+ INotificationManager service = getService();
+ try {
+ return service.getConversationNotificationChannel(mContext.getOpPackageName(),
+ mContext.getUserId(), mContext.getPackageName(), channelId, conversationId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns all notification channels belonging to the calling package.
*
* <p>Approved notification delegates (see {@link #canNotifyAsPackage(String)}) can query
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 8426374..dde6dda 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -26,7 +26,6 @@
import android.os.IBinder;
import android.os.IPullAtomCallback;
import android.os.IPullAtomResultReceiver;
-import android.os.IStatsCompanionService;
import android.os.IStatsManagerService;
import android.os.IStatsPullerCallback;
import android.os.IStatsd;
@@ -61,9 +60,6 @@
private IStatsd mService;
@GuardedBy("sLock")
- private IStatsCompanionService mStatsCompanion;
-
- @GuardedBy("sLock")
private IStatsManagerService mStatsManagerService;
/**
@@ -538,7 +534,7 @@
}
synchronized (sLock) {
try {
- IStatsCompanionService service = getIStatsCompanionServiceLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
PullAtomCallbackInternal rec =
new PullAtomCallbackInternal(atomTag, callback, executor);
service.registerPullAtomCallback(atomTag, coolDownNs, timeoutNs, additiveFields,
@@ -560,7 +556,7 @@
public void unregisterPullAtomCallback(int atomTag) {
synchronized (sLock) {
try {
- IStatsCompanionService service = getIStatsCompanionServiceLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
service.unregisterPullAtomCallback(atomTag);
} catch (RemoteException e) {
throw new RuntimeException("Unable to unregister pull atom callback");
@@ -746,16 +742,6 @@
}
@GuardedBy("sLock")
- private IStatsCompanionService getIStatsCompanionServiceLocked() {
- if (mStatsCompanion != null) {
- return mStatsCompanion;
- }
- mStatsCompanion = IStatsCompanionService.Stub.asInterface(
- ServiceManager.getService("statscompanion"));
- return mStatsCompanion;
- }
-
- @GuardedBy("sLock")
private IStatsManagerService getIStatsManagerServiceLocked() {
if (mStatsManagerService != null) {
return mStatsManagerService;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5cb2907..1b40a18 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3240,15 +3240,40 @@
}
/**
- * Same as {@link #bindService(Intent, ServiceConnection, int)}, but with an explicit userHandle
- * argument for use by system server and other multi-user aware code.
- * @hide
+ * Binds to a service in the given {@code user} in the same manner as
+ * {@link #bindService(Intent, ServiceConnection, int)}.
+ *
+ * <p>If the given {@code user} is in the same profile group and the target package is the
+ * same as the caller, {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} is
+ * sufficient. Otherwise, requires {@code android.Manifest.permission.INTERACT_ACROSS_USERS}
+ * for interacting with other users.
+ *
+ * @param service Identifies the service to connect to. The Intent must
+ * specify an explicit component name.
+ * @param conn Receives information as the service is started and stopped.
+ * This must be a valid ServiceConnection object; it must not be null.
+ * @param flags Operation options for the binding. May be 0,
+ * {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND},
+ * {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
+ * {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}.
+ * {@link #BIND_IMPORTANT}, or
+ * {@link #BIND_ADJUST_WITH_ACTIVITY}.
+ * @return {@code true} if the system is in the process of bringing up a
+ * service that your client has permission to bind to; {@code false}
+ * if the system couldn't find the service. If this value is {@code true}, you
+ * should later call {@link #unbindService} to release the
+ * connection.
+ *
+ * @throws SecurityException if the client does not have the required permission to bind.
*/
- @SystemApi
@SuppressWarnings("unused")
- @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
- public boolean bindServiceAsUser(@RequiresPermission Intent service, ServiceConnection conn,
- int flags, UserHandle user) {
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES
+ })
+ public boolean bindServiceAsUser(
+ @NonNull @RequiresPermission Intent service, @NonNull ServiceConnection conn, int flags,
+ @NonNull UserHandle user) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3bb0f92..c8f587f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6440,19 +6440,22 @@
*/
public static final int FLAG_RECEIVER_NO_ABORT = 0x08000000;
/**
- * If set, when sending a broadcast <i>before boot has completed</i> only
+ * If set, when sending a broadcast <i>before the system has fully booted up
+ * (which is even before {@link #ACTION_LOCKED_BOOT_COMPLETED} has been sent)"</i> only
* registered receivers will be called -- no BroadcastReceiver components
* will be launched. Sticky intent state will be recorded properly even
* if no receivers wind up being called. If {@link #FLAG_RECEIVER_REGISTERED_ONLY}
* is specified in the broadcast intent, this flag is unnecessary.
*
- * <p>This flag is only for use by system sevices as a convenience to
- * avoid having to implement a more complex mechanism around detection
+ * <p>This flag is only for use by system services (even services from mainline modules) as a
+ * convenience to avoid having to implement a more complex mechanism around detection
* of boot completion.
*
+ * <p>This is useful to system server mainline modules
+ *
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x04000000;
/**
* Set when this broadcast is for a boot upgrade, a special mode that
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 8231c58..43f3787 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -16,6 +16,7 @@
package android.hardware.soundtrigger;
+import android.annotation.Nullable;
import android.hardware.soundtrigger.ModelParams;
import android.media.AudioFormat;
import android.media.audio.common.AudioConfig;
@@ -32,8 +33,6 @@
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.annotation.Nullable;
-
import java.util.Arrays;
import java.util.UUID;
@@ -48,6 +47,7 @@
properties.description,
properties.uuid,
properties.version,
+ properties.supportedModelArch,
properties.maxSoundModels,
properties.maxKeyPhrases,
properties.maxUsers,
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 55505ba..d872009 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -101,6 +101,14 @@
/** Voice detection engine version */
public final int version;
+ /**
+ * String naming the architecture used for running the supported models.
+ * (eg. a platform running models on a DSP could implement this string to convey the DSP
+ * architecture used)
+ */
+ @NonNull
+ public final String supportedModelArch;
+
/** Maximum number of active sound models */
public final int maxSoundModels;
@@ -130,15 +138,17 @@
public final boolean returnsTriggerInEvent;
ModuleProperties(int id, @NonNull String implementor, @NonNull String description,
- @NonNull String uuid, int version, int maxSoundModels, int maxKeyphrases,
- int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
- int maxBufferMs, boolean supportsConcurrentCapture,
- int powerConsumptionMw, boolean returnsTriggerInEvent) {
+ @NonNull String uuid, int version, @NonNull String supportedModelArch,
+ int maxSoundModels, int maxKeyphrases, int maxUsers, int recognitionModes,
+ boolean supportsCaptureTransition, int maxBufferMs,
+ boolean supportsConcurrentCapture, int powerConsumptionMw,
+ boolean returnsTriggerInEvent) {
this.id = id;
this.implementor = requireNonNull(implementor);
this.description = requireNonNull(description);
this.uuid = UUID.fromString(requireNonNull(uuid));
this.version = version;
+ this.supportedModelArch = requireNonNull(supportedModelArch);
this.maxSoundModels = maxSoundModels;
this.maxKeyphrases = maxKeyphrases;
this.maxUsers = maxUsers;
@@ -167,6 +177,7 @@
String description = in.readString();
String uuid = in.readString();
int version = in.readInt();
+ String supportedModelArch = in.readString();
int maxSoundModels = in.readInt();
int maxKeyphrases = in.readInt();
int maxUsers = in.readInt();
@@ -177,7 +188,7 @@
int powerConsumptionMw = in.readInt();
boolean returnsTriggerInEvent = in.readByte() == 1;
return new ModuleProperties(id, implementor, description, uuid, version,
- maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
+ supportedModelArch, maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture,
powerConsumptionMw, returnsTriggerInEvent);
}
@@ -189,6 +200,7 @@
dest.writeString(description);
dest.writeString(uuid.toString());
dest.writeInt(version);
+ dest.writeString(supportedModelArch);
dest.writeInt(maxSoundModels);
dest.writeInt(maxKeyphrases);
dest.writeInt(maxUsers);
@@ -208,7 +220,8 @@
@Override
public String toString() {
return "ModuleProperties [id=" + id + ", implementor=" + implementor + ", description="
- + description + ", uuid=" + uuid + ", version=" + version + ", maxSoundModels="
+ + description + ", uuid=" + uuid + ", version=" + version
+ + " , supportedModelArch=" + supportedModelArch + ", maxSoundModels="
+ maxSoundModels + ", maxKeyphrases=" + maxKeyphrases + ", maxUsers="
+ maxUsers + ", recognitionModes=" + recognitionModes
+ ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs="
@@ -588,19 +601,19 @@
}
}
- /*****************************************************************************
+ /**
* A ModelParamRange is a representation of supported parameter range for a
* given loaded model.
- ****************************************************************************/
+ */
public static final class ModelParamRange implements Parcelable {
/**
- * start of supported range inclusive
+ * The inclusive start of supported range.
*/
public final int start;
/**
- * end of supported range inclusive
+ * The inclusive end of supported range.
*/
public final int end;
@@ -609,31 +622,65 @@
this.end = end;
}
+ /** @hide */
private ModelParamRange(@NonNull Parcel in) {
this.start = in.readInt();
this.end = in.readInt();
}
@NonNull
- public static final Creator<ModelParamRange> CREATOR = new Creator<ModelParamRange>() {
- @Override
- @NonNull
- public ModelParamRange createFromParcel(@NonNull Parcel in) {
- return new ModelParamRange(in);
- }
+ public static final Creator<ModelParamRange> CREATOR =
+ new Creator<ModelParamRange>() {
+ @Override
+ @NonNull
+ public ModelParamRange createFromParcel(@NonNull Parcel in) {
+ return new ModelParamRange(in);
+ }
- @Override
- @NonNull
- public ModelParamRange[] newArray(int size) {
- return new ModelParamRange[size];
- }
- };
+ @Override
+ @NonNull
+ public ModelParamRange[] newArray(int size) {
+ return new ModelParamRange[size];
+ }
+ };
+ /** @hide */
@Override
public int describeContents() {
return 0;
}
+ /** @hide */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (start);
+ result = prime * result + (end);
+ return result;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ModelParamRange other = (ModelParamRange) obj;
+ if (start != other.start) {
+ return false;
+ }
+ if (end != other.end) {
+ return false;
+ }
+ return true;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(start);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 5fe647c..5d80ab6 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -68,10 +68,9 @@
public static final int LOG_UID = 1007;
/**
- * Defines the UID/GID for the WIFI supplicant process.
- * @hide
+ * Defines the UID/GID for the WIFI native processes like wificond, supplicant, hostapd,
+ * vendor HAL, etc.
*/
- @UnsupportedAppUsage
public static final int WIFI_UID = 1010;
/**
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 67925bf..d7c6d0f 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -168,6 +168,22 @@
public static final int RECOGNITION_MODE_USER_IDENTIFICATION
= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "MODEL_PARAM_" }, value = {
+ MODEL_PARAM_THRESHOLD_FACTOR,
+ })
+ public @interface ModelParams {}
+
+ /**
+ * Controls the sensitivity threshold adjustment factor for a given model.
+ * Negative value corresponds to less sensitive model (high threshold) and
+ * a positive value corresponds to a more sensitive model (low threshold).
+ * Default value is 0.
+ */
+ public static final int MODEL_PARAM_THRESHOLD_FACTOR =
+ android.hardware.soundtrigger.ModelParams.THRESHOLD_FACTOR;
+
static final String TAG = "AlwaysOnHotwordDetector";
static final boolean DBG = false;
@@ -198,6 +214,53 @@
private int mAvailability = STATE_NOT_READY;
/**
+ * A ModelParamRange is a representation of supported parameter range for a
+ * given loaded model.
+ */
+ public static final class ModelParamRange {
+ private final SoundTrigger.ModelParamRange mModelParamRange;
+
+ /** @hide */
+ ModelParamRange(SoundTrigger.ModelParamRange modelParamRange) {
+ mModelParamRange = modelParamRange;
+ }
+
+ /**
+ * The inclusive start of supported range.
+ *
+ * @return start of range
+ */
+ public int start() {
+ return mModelParamRange.start;
+ }
+
+ /**
+ * The inclusive end of supported range.
+ *
+ * @return end of range
+ */
+ public int end() {
+ return mModelParamRange.end;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return mModelParamRange.toString();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ return mModelParamRange.equals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return mModelParamRange.hashCode();
+ }
+ }
+
+ /**
* Additional payload for {@link Callback#onDetected}.
*/
public static class EventPayload {
@@ -445,6 +508,83 @@
}
/**
+ * Set a model specific {@link ModelParams} with the given value. This
+ * parameter will keep its value for the duration the model is loaded regardless of starting and
+ * stopping recognition. Once the model is unloaded, the value will be lost.
+ * {@link AlwaysOnHotwordDetector#queryParameter} should be checked first before calling this
+ * method.
+ *
+ * @param modelParam {@link ModelParams}
+ * @param value Value to set
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+ * if API is not supported by HAL
+ */
+ public int setParameter(@ModelParams int modelParam, int value) {
+ if (DBG) {
+ Slog.d(TAG, "setParameter(" + modelParam + ", " + value + ")");
+ }
+
+ synchronized (mLock) {
+ if (mAvailability == STATE_INVALID) {
+ throw new IllegalStateException("setParameter called on an invalid detector");
+ }
+
+ return setParameterLocked(modelParam, value);
+ }
+ }
+
+ /**
+ * Get a model specific {@link ModelParams}. This parameter will keep its value
+ * for the duration the model is loaded regardless of starting and stopping recognition.
+ * Once the model is unloaded, the value will be lost. If the value is not set, a default
+ * value is returned. See {@link ModelParams} for parameter default values.
+ * {@link AlwaysOnHotwordDetector#queryParameter} should be checked first before
+ * calling this method.
+ *
+ * @param modelParam {@link ModelParams}
+ * @return value of parameter
+ */
+ public int getParameter(@ModelParams int modelParam) {
+ if (DBG) {
+ Slog.d(TAG, "getParameter(" + modelParam + ")");
+ }
+
+ synchronized (mLock) {
+ if (mAvailability == STATE_INVALID) {
+ throw new IllegalStateException("getParameter called on an invalid detector");
+ }
+
+ return getParameterLocked(modelParam);
+ }
+ }
+
+ /**
+ * Determine if parameter control is supported for the given model handle.
+ * This method should be checked prior to calling {@link AlwaysOnHotwordDetector#setParameter}
+ * or {@link AlwaysOnHotwordDetector#getParameter}.
+ *
+ * @param modelParam {@link ModelParams}
+ * @return supported range of parameter, null if not supported
+ */
+ @Nullable
+ public ModelParamRange queryParameter(@ModelParams int modelParam) {
+ if (DBG) {
+ Slog.d(TAG, "queryParameter(" + modelParam + ")");
+ }
+
+ synchronized (mLock) {
+ if (mAvailability == STATE_INVALID) {
+ throw new IllegalStateException("queryParameter called on an invalid detector");
+ }
+
+ return queryParameterLocked(modelParam);
+ }
+ }
+
+ /**
* Creates an intent to start the enrollment for the associated keyphrase.
* This intent must be invoked using {@link Context#startForegroundService(Intent)}.
* Starting re-enrollment is only valid if the keyphrase is un-enrolled,
@@ -601,6 +741,47 @@
return code;
}
+ private int setParameterLocked(@ModelParams int modelParam, int value) {
+ try {
+ int code = mModelManagementService.setParameter(mVoiceInteractionService,
+ mKeyphraseMetadata.id, modelParam, value);
+
+ if (code != STATUS_OK) {
+ Slog.w(TAG, "setParameter failed with error code " + code);
+ }
+
+ return code;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private int getParameterLocked(@ModelParams int modelParam) {
+ try {
+ return mModelManagementService.getParameter(mVoiceInteractionService,
+ mKeyphraseMetadata.id, modelParam);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Nullable
+ private ModelParamRange queryParameterLocked(@ModelParams int modelParam) {
+ try {
+ SoundTrigger.ModelParamRange modelParamRange =
+ mModelManagementService.queryParameter(mVoiceInteractionService,
+ mKeyphraseMetadata.id, modelParam);
+
+ if (modelParamRange == null) {
+ return null;
+ }
+
+ return new ModelParamRange(modelParamRange);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private void notifyStateChangedLocked() {
Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED);
message.arg1 = mAvailability;
diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java
index 6fdadc6..27edb0b 100644
--- a/core/java/android/view/InsetsAnimationControlCallbacks.java
+++ b/core/java/android/view/InsetsAnimationControlCallbacks.java
@@ -16,17 +16,28 @@
package android.view;
+import android.view.InsetsController.LayoutInsetsDuringAnimation;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
+
/**
* Provide an interface to let InsetsAnimationControlImpl call back into its owner.
* @hide
*/
public interface InsetsAnimationControlCallbacks {
+
/**
- * Dispatch the animation started event to all listeners.
- * @param animation
+ * Executes the necessary code to start the animation in the correct order, including:
+ * <ul>
+ * <li>Dispatch {@link WindowInsetsAnimationCallback#onPrepare}</li>
+ * <li>Update insets state and run layout according to {@code layoutDuringAnimation}</li>
+ * <li>Dispatch {@link WindowInsetsAnimationCallback#onStart}</li>
+ * <li>Dispatch {@link WindowInsetsAnimationControlListener#onReady}</li>
+ * </ul>
*/
- void dispatchAnimationStarted(WindowInsetsAnimationCallback.InsetsAnimation animation,
- WindowInsetsAnimationCallback.AnimationBounds bounds);
+ void startAnimation(InsetsAnimationControlImpl controller,
+ WindowInsetsAnimationControlListener listener, int types, InsetsAnimation animation,
+ AnimationBounds bounds, @LayoutInsetsDuringAnimation int layoutDuringAnimation);
/**
* Schedule the apply by posting the animation callback.
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index a3245b9..6589e75 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_FLOATING;
import static android.view.InsetsState.ISIDE_LEFT;
@@ -30,6 +32,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
+import android.view.InsetsController.LayoutInsetsDuringAnimation;
import android.view.InsetsState.InternalInsetsSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
@@ -80,7 +83,8 @@
public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame,
InsetsState state, WindowInsetsAnimationControlListener listener,
@InsetsType int types,
- InsetsAnimationControlCallbacks controller, long durationMs, boolean fade) {
+ InsetsAnimationControlCallbacks controller, long durationMs, boolean fade,
+ @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
mControls = controls;
mListener = listener;
mTypes = types;
@@ -95,14 +99,11 @@
mFrame = new Rect(frame);
buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls);
- // TODO: Check for controllability first and wait for IME if needed.
- listener.onReady(this, types);
-
mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes,
InsetsController.INTERPOLATOR, durationMs);
mAnimation.setAlpha(getCurrentAlpha());
- mController.dispatchAnimationStarted(mAnimation,
- new AnimationBounds(mHiddenInsets, mShownInsets));
+ mController.startAnimation(this, listener, types, mAnimation,
+ new AnimationBounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation);
}
@Override
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 2a7a4e3..775490c 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -37,16 +37,20 @@
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
+import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
/**
@@ -67,6 +71,37 @@
private @interface AnimationDirection{}
/**
+ * Layout mode during insets animation: The views should be laid out as if the changing inset
+ * types are fully shown. Before starting the animation, {@link View#onApplyWindowInsets} will
+ * be called as if the changing insets types are shown, which will result in the views being
+ * laid out as if the insets are fully shown.
+ */
+ static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
+
+ /**
+ * Layout mode during insets animation: The views should be laid out as if the changing inset
+ * types are fully hidden. Before starting the animation, {@link View#onApplyWindowInsets} will
+ * be called as if the changing insets types are hidden, which will result in the views being
+ * laid out as if the insets are fully hidden.
+ */
+ static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
+
+ /**
+ * Determines the behavior of how the views should be laid out during an insets animation that
+ * is controlled by the application by calling {@link #controlWindowInsetsAnimation}.
+ * <p>
+ * When the animation is system-initiated, the layout mode is always chosen such that the
+ * pre-animation layout will represent the opposite of the starting state, i.e. when insets
+ * are appearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_SHOWN} will be used. When insets
+ * are disappearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_HIDDEN} will be used.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {LAYOUT_INSETS_DURING_ANIMATION_SHOWN,
+ LAYOUT_INSETS_DURING_ANIMATION_HIDDEN})
+ @interface LayoutInsetsDuringAnimation {
+ }
+
+ /**
* Translation animation evaluator.
*/
private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of(
@@ -109,11 +144,7 @@
@Override
public void onReady(WindowInsetsAnimationController controller, int types) {
mController = controller;
- if (mShow) {
- showDirectly(types);
- } else {
- hideDirectly(types);
- }
+
mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE;
mAnimator = ObjectAnimator.ofObject(
controller,
@@ -131,7 +162,9 @@
onAnimationFinish();
}
});
+ mStartingAnimation = true;
mAnimator.start();
+ mStartingAnimation = false;
}
@Override
@@ -185,6 +218,7 @@
private int mPendingTypesToShow;
private int mLastLegacySoftInputMode;
+ private boolean mStartingAnimation;
private SyncRtSurfaceTransactionApplier mApplier;
@@ -266,6 +300,14 @@
}
/**
+ * @see InsetsState#calculateVisibleInsets(Rect, Rect, int)
+ */
+ public Rect calculateVisibleInsets(Rect legacyVisibleInsets,
+ @SoftInputModeFlags int softInputMode) {
+ return mState.calculateVisibleInsets(mFrame, legacyVisibleInsets, softInputMode);
+ }
+
+ /**
* Called when the server has dispatched us a new set of inset controls.
*/
public void onControlsChanged(InsetsSourceControl[] activeControls) {
@@ -312,7 +354,7 @@
// Only one animator (with multiple InsetsType) can run at a time.
// previous one should be cancelled for simplicity.
cancelExistingAnimation();
- } else if (consumer.isVisible()
+ } else if (consumer.isRequestedVisible()
&& (mAnimationDirection == DIRECTION_NONE
|| mAnimationDirection == DIRECTION_HIDE)) {
// no-op: already shown or animating in (because window visibility is
@@ -338,7 +380,7 @@
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
if (mAnimationDirection == DIRECTION_SHOW) {
cancelExistingAnimation();
- } else if (!consumer.isVisible()
+ } else if (!consumer.isRequestedVisible()
&& (mAnimationDirection == DIRECTION_NONE
|| mAnimationDirection == DIRECTION_HIDE)) {
// no-op: already hidden or animating out.
@@ -363,12 +405,14 @@
listener.onCancelled();
return;
}
- controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */);
+ controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */,
+ getLayoutInsetsDuringAnimationMode(types));
}
private void controlAnimationUnchecked(@InsetsType int types,
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
- long durationMs, boolean fade) {
+ long durationMs, boolean fade,
+ @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
if (types == 0) {
// nothing to animate.
return;
@@ -398,7 +442,8 @@
}
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
- frame, mState, listener, typesReady, this, durationMs, fade);
+ frame, mState, listener, typesReady, this, durationMs, fade,
+ layoutInsetsDuringAnimation);
mAnimationControls.add(controller);
}
@@ -412,7 +457,7 @@
boolean isReady = true;
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
- boolean setVisible = !consumer.isVisible();
+ boolean setVisible = !consumer.isRequestedVisible();
if (setVisible) {
// Show request
switch(consumer.requestShow(fromIme)) {
@@ -454,6 +499,29 @@
return typesReady;
}
+ private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
+ @InsetsType int types) {
+
+ final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+
+ // Generally, we want to layout the opposite of the current state. This is to make animation
+ // callbacks easy to use: The can capture the layout values and then treat that as end-state
+ // during the animation.
+ //
+ // However, if controlling multiple sources, we want to treat it as shown if any of the
+ // types is currently hidden.
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ InsetsSourceConsumer consumer = mSourceConsumers.get(internalTypes.valueAt(i));
+ if (consumer == null) {
+ continue;
+ }
+ if (!consumer.isRequestedVisible()) {
+ return LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
+ }
+ }
+ return LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
+ }
+
private void cancelExistingControllers(@InsetsType int types) {
for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
InsetsAnimationControlImpl control = mAnimationControls.get(i);
@@ -597,7 +665,9 @@
// and hidden state insets are correct.
controlAnimationUnchecked(
types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
- true /* fade */);
+ true /* fade */, show
+ ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+ : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
}
private void hideDirectly(@InsetsType int types) {
@@ -629,18 +699,40 @@
@VisibleForTesting
@Override
- public void dispatchAnimationStarted(InsetsAnimation animation, AnimationBounds bounds) {
- mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation, bounds);
+ public void startAnimation(InsetsAnimationControlImpl controller,
+ WindowInsetsAnimationControlListener listener, int types, InsetsAnimation animation,
+ AnimationBounds bounds, int layoutDuringAnimation) {
+ if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
+ showDirectly(types);
+ } else {
+ hideDirectly(types);
+ }
+ mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation);
+ mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this);
+ mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds);
+ listener.onReady(controller, types);
+ return true;
+ }
+ });
+ mViewRoot.mView.invalidate();
}
@VisibleForTesting
public void dispatchAnimationFinished(InsetsAnimation animation) {
- mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
+ mViewRoot.mView.dispatchWindowInsetsAnimationFinish(animation);
}
@VisibleForTesting
@Override
public void scheduleApplyChangeInsets() {
+ if (mStartingAnimation) {
+ mAnimCallback.run();
+ mAnimCallbackScheduled = false;
+ return;
+ }
if (!mAnimCallbackScheduled) {
mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
mAnimCallback, null /* token*/);
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 324d562..67ccfd6 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
@@ -23,6 +24,7 @@
import android.view.InsetsState.InternalInsetsType;
import java.io.PrintWriter;
+import java.util.Objects;
/**
* Represents the state of a single window generating insets for clients.
@@ -34,6 +36,7 @@
/** Frame of the source in screen coordinate space */
private final Rect mFrame;
+ private @Nullable Rect mVisibleFrame;
private boolean mVisible;
private final Rect mTmpFrame = new Rect();
@@ -54,6 +57,10 @@
mFrame.set(frame);
}
+ public void setVisibleFrame(@Nullable Rect visibleFrame) {
+ mVisibleFrame = visibleFrame != null ? new Rect(visibleFrame) : visibleFrame;
+ }
+
public void setVisible(boolean visible) {
mVisible = visible;
}
@@ -66,6 +73,10 @@
return mFrame;
}
+ public @Nullable Rect getVisibleFrame() {
+ return mVisibleFrame;
+ }
+
public boolean isVisible() {
return mVisible;
}
@@ -79,10 +90,22 @@
* source.
*/
public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
+ return calculateInsets(relativeFrame, mFrame, ignoreVisibility);
+ }
+
+ /**
+ * Like {@link #calculateInsets(Rect, boolean)}, but will return visible insets.
+ */
+ public Insets calculateVisibleInsets(Rect relativeFrame) {
+ return calculateInsets(relativeFrame, mVisibleFrame != null ? mVisibleFrame : mFrame,
+ false /* ignoreVisibility */);
+ }
+
+ private Insets calculateInsets(Rect relativeFrame, Rect frame, boolean ignoreVisibility) {
if (!ignoreVisibility && !mVisible) {
return Insets.NONE;
}
- if (!mTmpFrame.setIntersect(mFrame, relativeFrame)) {
+ if (!mTmpFrame.setIntersect(frame, relativeFrame)) {
return Insets.NONE;
}
@@ -110,6 +133,9 @@
pw.print(prefix);
pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
pw.print(" frame="); pw.print(mFrame.toShortString());
+ if (mVisibleFrame != null) {
+ pw.print(" visibleFrmae="); pw.print(mVisibleFrame.toShortString());
+ }
pw.print(" visible="); pw.print(mVisible);
pw.println();
}
@@ -123,6 +149,7 @@
if (mType != that.mType) return false;
if (mVisible != that.mVisible) return false;
+ if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false;
return mFrame.equals(that.mFrame);
}
@@ -137,6 +164,7 @@
public InsetsSource(Parcel in) {
mType = in.readInt();
mFrame = in.readParcelable(null /* loader */);
+ mVisibleFrame = in.readParcelable(null /* loader */);
mVisible = in.readBoolean();
}
@@ -149,6 +177,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
dest.writeParcelable(mFrame, 0 /* flags*/);
+ dest.writeParcelable(mVisibleFrame, 0 /* flags */);
dest.writeBoolean(mVisible);
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index c6d9898..b2a5d91 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -53,7 +53,7 @@
}
protected final InsetsController mController;
- protected boolean mVisible;
+ protected boolean mRequestedVisible;
private final Supplier<Transaction> mTransactionSupplier;
private final @InternalInsetsType int mType;
private final InsetsState mState;
@@ -66,7 +66,7 @@
mState = state;
mTransactionSupplier = transactionSupplier;
mController = controller;
- mVisible = InsetsState.getDefaultVisibility(type);
+ mRequestedVisible = InsetsState.getDefaultVisibility(type);
}
public void setControl(@Nullable InsetsSourceControl control) {
@@ -94,12 +94,12 @@
@VisibleForTesting
public void show() {
- setVisible(true);
+ setRequestedVisible(true);
}
@VisibleForTesting
public void hide() {
- setVisible(false);
+ setRequestedVisible(false);
}
/**
@@ -126,16 +126,16 @@
if (mSourceControl == null) {
return false;
}
- if (mState.getSource(mType).isVisible() == mVisible) {
+ if (mState.getSource(mType).isVisible() == mRequestedVisible) {
return false;
}
- mState.getSource(mType).setVisible(mVisible);
+ mState.getSource(mType).setVisible(mRequestedVisible);
return true;
}
@VisibleForTesting
- public boolean isVisible() {
- return mVisible;
+ public boolean isRequestedVisible() {
+ return mRequestedVisible;
}
/**
@@ -157,11 +157,15 @@
// no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
}
- private void setVisible(boolean visible) {
- if (mVisible == visible) {
+ /**
+ * Sets requested visibility from the client, regardless of whether we are able to control it at
+ * the moment.
+ */
+ private void setRequestedVisible(boolean requestedVisible) {
+ if (mRequestedVisible == requestedVisible) {
return;
}
- mVisible = visible;
+ mRequestedVisible = requestedVisible;
applyLocalVisibilityOverride();
mController.notifyVisibilityChanged();
}
@@ -173,7 +177,7 @@
}
final Transaction t = mTransactionSupplier.get();
- if (mVisible) {
+ if (mRequestedVisible) {
t.show(mSourceControl.getLeash());
} else {
t.hide(mSourceControl.getLeash());
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index ae1e579..e33ca70 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -19,12 +19,14 @@
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.IME;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.indexOf;
+import static android.view.WindowInsets.Type.isVisibleInsetsType;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
@@ -41,6 +43,7 @@
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -186,6 +189,32 @@
: systemBars());
}
+ public Rect calculateVisibleInsets(Rect frame, Rect legacyVisibleInsets,
+ @SoftInputModeFlags int softInputMode) {
+ if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
+ return legacyVisibleInsets;
+ }
+
+ Insets insets = Insets.NONE;
+ for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ InsetsSource source = mSources.get(type);
+ if (source == null) {
+ continue;
+ }
+ if (sNewInsetsMode != NEW_INSETS_MODE_FULL && type != ITYPE_IME) {
+ continue;
+ }
+
+ // Ignore everything that's not a system bar or IME.
+ int publicType = InsetsState.toPublicType(type);
+ if (!isVisibleInsetsType(publicType, softInputMode)) {
+ continue;
+ }
+ insets = Insets.max(source.calculateVisibleInsets(frame), insets);
+ }
+ return insets.toRect();
+ }
+
private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap,
@Nullable boolean[] typeVisibilityMap) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0db80e2..13d609b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11117,7 +11117,21 @@
}
/**
- * Dispatches {@link WindowInsetsAnimationCallback#onStarted(InsetsAnimation, AnimationBounds)}
+ * Dispatches {@link WindowInsetsAnimationCallback#onPrepare(InsetsAnimation)}
+ * when Window Insets animation is being prepared.
+ * @param animation current animation
+ *
+ * @see WindowInsetsAnimationCallback#onPrepare(InsetsAnimation)
+ */
+ public void dispatchWindowInsetsAnimationPrepare(
+ @NonNull InsetsAnimation animation) {
+ if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
+ mListenerInfo.mWindowInsetsAnimationCallback.onPrepare(animation);
+ }
+ }
+
+ /**
+ * Dispatches {@link WindowInsetsAnimationCallback#onStart(InsetsAnimation, AnimationBounds)}
* when Window Insets animation is started.
* @param animation current animation
* @param bounds the upper and lower {@link AnimationBounds} that provides range of
@@ -11125,10 +11139,10 @@
* @return the upper and lower {@link AnimationBounds}.
*/
@NonNull
- public AnimationBounds dispatchWindowInsetsAnimationStarted(
+ public AnimationBounds dispatchWindowInsetsAnimationStart(
@NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
- return mListenerInfo.mWindowInsetsAnimationCallback.onStarted(animation, bounds);
+ return mListenerInfo.mWindowInsetsAnimationCallback.onStart(animation, bounds);
}
return bounds;
}
@@ -11149,13 +11163,13 @@
}
/**
- * Dispatches {@link WindowInsetsAnimationCallback#onFinished(InsetsAnimation)}
+ * Dispatches {@link WindowInsetsAnimationCallback#onFinish(InsetsAnimation)}
* when Window Insets animation finishes.
* @param animation The current ongoing {@link InsetsAnimation}.
*/
- public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) {
+ public void dispatchWindowInsetsAnimationFinish(@NonNull InsetsAnimation animation) {
if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
- mListenerInfo.mWindowInsetsAnimationCallback.onFinished(animation);
+ mListenerInfo.mWindowInsetsAnimationCallback.onFinish(animation);
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 5fb7177..047d7da 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7199,13 +7199,23 @@
}
@Override
- @NonNull
- public AnimationBounds dispatchWindowInsetsAnimationStarted(
- @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
- super.dispatchWindowInsetsAnimationStarted(animation, bounds);
+ public void dispatchWindowInsetsAnimationPrepare(
+ @NonNull InsetsAnimation animation) {
+ super.dispatchWindowInsetsAnimationPrepare(animation);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
- getChildAt(i).dispatchWindowInsetsAnimationStarted(animation, bounds);
+ getChildAt(i).dispatchWindowInsetsAnimationPrepare(animation);
+ }
+ }
+
+ @Override
+ @NonNull
+ public AnimationBounds dispatchWindowInsetsAnimationStart(
+ @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
+ super.dispatchWindowInsetsAnimationStart(animation, bounds);
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ getChildAt(i).dispatchWindowInsetsAnimationStart(animation, bounds);
}
return bounds;
}
@@ -7222,11 +7232,11 @@
}
@Override
- public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) {
- super.dispatchWindowInsetsAnimationFinished(animation);
+ public void dispatchWindowInsetsAnimationFinish(@NonNull InsetsAnimation animation) {
+ super.dispatchWindowInsetsAnimationFinish(animation);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
- getChildAt(i).dispatchWindowInsetsAnimationFinished(animation);
+ getChildAt(i).dispatchWindowInsetsAnimationFinish(animation);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index bf8dc65..ca8ba4c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1464,6 +1464,7 @@
return;
}
mApplyInsetsRequested = true;
+ requestLayout();
// If this changes during traversal, no need to schedule another one as it will dispatch it
// during the current traversal.
@@ -2102,6 +2103,12 @@
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
+ private void updateVisibleInsets() {
+ Rect visibleInsets = mInsetsController.calculateVisibleInsets(mPendingVisibleInsets,
+ mWindowAttributes.softInputMode);
+ mAttachInfo.mVisibleInsets.set(visibleInsets);
+ }
+
InsetsController getInsetsController() {
return mInsetsController;
}
@@ -2250,7 +2257,7 @@
insetsChanged = true;
}
if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
- mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
+ updateVisibleInsets();
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
@@ -2316,6 +2323,7 @@
if (mApplyInsetsRequested) {
mApplyInsetsRequested = false;
+ updateVisibleInsets();
dispatchApplyInsets(host);
if (mLayoutRequested) {
// Short-circuit catching a new layout request here, so
@@ -2500,7 +2508,7 @@
contentInsetsChanged = true;
}
if (visibleInsetsChanged) {
- mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
+ updateVisibleInsets();
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 9df131d..9291b56 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -29,6 +29,8 @@
import static android.view.WindowInsets.Type.all;
import static android.view.WindowInsets.Type.indexOf;
import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -40,6 +42,7 @@
import android.graphics.Rect;
import android.util.SparseArray;
import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethod;
@@ -1289,6 +1292,17 @@
public static @InsetsType int all() {
return 0xFFFFFFFF;
}
+
+ /**
+ * Checks whether the specified type is considered to be part of visible insets.
+ * @hide
+ */
+ public static boolean isVisibleInsetsType(int type,
+ @SoftInputModeFlags int softInputModeFlags) {
+ int softInputMode = softInputModeFlags & SOFT_INPUT_MASK_ADJUST;
+ return (type & Type.systemBars()) != 0
+ || (softInputMode != SOFT_INPUT_ADJUST_NOTHING && (type & Type.ime()) != 0);
+ }
}
/**
diff --git a/core/java/android/view/WindowInsetsAnimationCallback.java b/core/java/android/view/WindowInsetsAnimationCallback.java
index 5e71f27..e84c3e3 100644
--- a/core/java/android/view/WindowInsetsAnimationCallback.java
+++ b/core/java/android/view/WindowInsetsAnimationCallback.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Insets;
+import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;
@@ -30,7 +31,47 @@
public interface WindowInsetsAnimationCallback {
/**
- * Called when an inset animation gets started.
+ * Called when an insets animation is about to start and before the views have been laid out in
+ * the end state of the animation. The ordering of events during an insets animation is the
+ * following:
+ * <p>
+ * <ul>
+ * <li>Application calls {@link WindowInsetsController#hideInputMethod()},
+ * {@link WindowInsetsController#showInputMethod()},
+ * {@link WindowInsetsController#controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)}</li>
+ * <li>onPrepare is called on the view hierarchy listeners</li>
+ * <li>{@link View#onApplyWindowInsets} will be called with the end state of the
+ * animation</li>
+ * <li>View hierarchy gets laid out according to the changes the application has requested
+ * due to the new insets being dispatched</li>
+ * <li>{@link #onStart} is called <em>before</em> the view
+ * hierarchy gets drawn in the new laid out state</li>
+ * <li>{@link #onProgress} is called immediately after with the animation start state</li>
+ * <li>The frame gets drawn.</li>
+ * </ul>
+ * <p>
+ * This ordering allows the application to inspect the end state after the animation has
+ * finished, and then revert to the starting state of the animation in the first
+ * {@link #onProgress} callback by using post-layout view properties like {@link View#setX} and
+ * related methods.
+ * <p>
+ * Note: If the animation is application controlled by using
+ * {@link WindowInsetsController#controlInputMethodAnimation}, the end state of the animation
+ * is undefined as the application may decide on the end state only by passing in the
+ * {@code shown} parameter when calling {@link WindowInsetsAnimationController#finish}. In this
+ * situation, the system will dispatch the insets in the opposite visibility state before the
+ * animation starts. Example: When controlling the input method with
+ * {@link WindowInsetsController#controlInputMethodAnimation} and the input method is currently
+ * showing, {@link View#onApplyWindowInsets} will receive a {@link WindowInsets} instance for
+ * which {@link WindowInsets#isVisible} will return {@code false} for {@link Type#ime}.
+ *
+ * @param animation The animation that is about to start.
+ */
+ default void onPrepare(@NonNull InsetsAnimation animation) {
+ }
+
+ /**
+ * Called when an insets animation gets started.
* <p>
* Note that, like {@link #onProgress}, dispatch of the animation start event is hierarchical:
* It will starts at the root of the view hierarchy and then traverse it and invoke the callback
@@ -45,7 +86,7 @@
* subtree of the hierarchy.
*/
@NonNull
- default AnimationBounds onStarted(
+ default AnimationBounds onStart(
@NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
return bounds;
}
@@ -72,12 +113,12 @@
WindowInsets onProgress(@NonNull WindowInsets insets);
/**
- * Called when an inset animation has finished.
+ * Called when an insets animation has finished.
*
* @param animation The animation that has finished running. This will be the same instance as
- * passed into {@link #onStarted}
+ * passed into {@link #onStart}
*/
- default void onFinished(@NonNull InsetsAnimation animation) {
+ default void onFinish(@NonNull InsetsAnimation animation) {
}
/**
@@ -253,14 +294,14 @@
/**
* Insets both the lower and upper bound by the specified insets. This is to be used in
- * {@link WindowInsetsAnimationCallback#onStarted} to indicate that a part of the insets has
+ * {@link WindowInsetsAnimationCallback#onStart} to indicate that a part of the insets has
* been used to offset or clip its children, and the children shouldn't worry about that
* part anymore.
*
* @param insets The amount to inset.
* @return A copy of this instance inset in the given directions.
* @see WindowInsets#inset
- * @see WindowInsetsAnimationCallback#onStarted
+ * @see WindowInsetsAnimationCallback#onStart
*/
@NonNull
public AnimationBounds inset(@NonNull Insets insets) {
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index 8a226c1..f91254d 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Hide;
import android.annotation.NonNull;
import android.view.WindowInsets.Type.InsetsType;
import android.view.inputmethod.EditorInfo;
@@ -26,6 +27,12 @@
public interface WindowInsetsAnimationControlListener {
/**
+ * @hide
+ */
+ default void onPrepare(int types) {
+ }
+
+ /**
* Called when the animation is ready to be controlled. This may be delayed when the IME needs
* to redraw because of an {@link EditorInfo} change, or when the window is starting up.
*
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 6de56be..9d7f292 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -149,7 +149,8 @@
*
* @param types The {@link InsetsType}s the application has requested to control.
* @param durationMillis duration of animation in
- * {@link java.util.concurrent.TimeUnit#MILLISECONDS}
+ * {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
+ * animation doesn't have a predetermined duration.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* windows are ready to be controlled, among other callbacks.
* @hide
@@ -162,7 +163,8 @@
* modifying the position of the IME when it's causing insets.
*
* @param durationMillis duration of the animation in
- * {@link java.util.concurrent.TimeUnit#MILLISECONDS}
+ * {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
+ * animation doesn't have a predetermined duration.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* IME are ready to be controlled, among other callbacks.
*/
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index a0cf534..20af76b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5921,8 +5921,6 @@
// The offsets of that last touch down event. Remembered to start selection there.
private int mMinTouchOffset, mMaxTouchOffset;
- private boolean mGestureStayedInTapRegion;
-
// Where the user first starts the drag motion.
private int mStartOffset = -1;
@@ -6029,8 +6027,7 @@
eventX, eventY);
// Double tap detection
- if (mGestureStayedInTapRegion
- && mTouchState.isMultiTapInSameArea()
+ if (mTouchState.isMultiTapInSameArea()
&& (isMouse || isPositionOnText(eventX, eventY))) {
if (TextView.DEBUG_CURSOR) {
logCursor("SelectionModifierCursorController: onTouchEvent",
@@ -6043,7 +6040,6 @@
}
mDiscardNextActionUp = true;
}
- mGestureStayedInTapRegion = true;
mHaventMovedEnoughToStartDrag = true;
}
break;
@@ -6059,25 +6055,8 @@
break;
case MotionEvent.ACTION_MOVE:
- final ViewConfiguration viewConfig = ViewConfiguration.get(
- mTextView.getContext());
-
- if (mGestureStayedInTapRegion || mHaventMovedEnoughToStartDrag) {
- final float deltaX = eventX - mTouchState.getLastDownX();
- final float deltaY = eventY - mTouchState.getLastDownY();
- final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
-
- if (mGestureStayedInTapRegion) {
- int doubleTapTouchSlop = viewConfig.getScaledDoubleTapTouchSlop();
- mGestureStayedInTapRegion =
- distanceSquared <= doubleTapTouchSlop * doubleTapTouchSlop;
- }
- if (mHaventMovedEnoughToStartDrag) {
- // We don't start dragging until the user has moved enough.
- int touchSlop = viewConfig.getScaledTouchSlop();
- mHaventMovedEnoughToStartDrag =
- distanceSquared <= touchSlop * touchSlop;
- }
+ if (mHaventMovedEnoughToStartDrag) {
+ mHaventMovedEnoughToStartDrag = !mTouchState.isMovedEnoughForDrag();
}
if (isMouse && !isDragAcceleratorActive()) {
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index d53099d..6277afe 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -31,13 +31,15 @@
import java.lang.annotation.RetentionPolicy;
/**
- * Helper class used by {@link Editor} to track state for touch events.
+ * Helper class used by {@link Editor} to track state for touch events. Ideally the logic here
+ * should be replaced with {@link android.view.GestureDetector}.
*
* @hide
*/
@VisibleForTesting(visibility = PACKAGE)
public class EditorTouchState {
private float mLastDownX, mLastDownY;
+ private long mLastDownMillis;
private float mLastUpX, mLastUpY;
private long mLastUpMillis;
@@ -106,9 +108,18 @@
final int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
+
+ // We check both the time between the last up and current down event, as well as the
+ // time between the first down and up events. The latter check is necessary to handle
+ // the case when the user taps, drags/holds for some time, and then lifts up and
+ // quickly taps in the same area. This scenario should not be treated as a double-tap.
+ // This follows the behavior in GestureDetector.
final long millisSinceLastUp = event.getEventTime() - mLastUpMillis;
+ final long millisBetweenLastDownAndLastUp = mLastUpMillis - mLastDownMillis;
+
// Detect double tap and triple click.
if (millisSinceLastUp <= ViewConfiguration.getDoubleTapTimeout()
+ && millisBetweenLastDownAndLastUp <= ViewConfiguration.getDoubleTapTimeout()
&& (mMultiTapStatus == MultiTapStatus.FIRST_TAP
|| (mMultiTapStatus == MultiTapStatus.DOUBLE_TAP && isMouse))) {
if (mMultiTapStatus == MultiTapStatus.FIRST_TAP) {
@@ -133,6 +144,7 @@
}
mLastDownX = event.getX();
mLastDownY = event.getY();
+ mLastDownMillis = event.getEventTime();
mMovedEnoughForDrag = false;
mIsDragCloseToVertical = false;
} else if (action == MotionEvent.ACTION_UP) {
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 0a0d05c..de204ba 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -16,6 +16,7 @@
package com.android.internal.app;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
import static android.view.accessibility.AccessibilityManager.ShortcutType;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -24,13 +25,17 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlertDialog;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.os.UserHandle;
import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -49,7 +54,10 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import java.util.StringJoiner;
/**
* Activity used to display and persist a service or feature target for the Accessibility button.
@@ -59,12 +67,46 @@
private static final String MAGNIFICATION_COMPONENT_ID =
"com.android.server.accessibility.MagnificationController";
+ private static final char SERVICES_SEPARATOR = ':';
+ private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
+ new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR);
+ private static final int ACCESSIBILITY_BUTTON_USER_TYPE = convertToUserType(
+ ACCESSIBILITY_BUTTON);
+ private static final int ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE = convertToUserType(
+ ACCESSIBILITY_SHORTCUT_KEY);
+
private int mShortcutType;
private List<AccessibilityButtonTarget> mTargets = new ArrayList<>();
private AlertDialog mAlertDialog;
private TargetAdapter mTargetAdapter;
/**
+ * Annotation for different user shortcut type UI type.
+ *
+ * {@code DEFAULT} for displaying default value.
+ * {@code SOFTWARE} for displaying specifying the accessibility services or features which
+ * choose accessibility button in the navigation bar as preferred shortcut.
+ * {@code HARDWARE} for displaying specifying the accessibility services or features which
+ * choose accessibility shortcut as preferred shortcut.
+ * {@code TRIPLETAP} for displaying specifying magnification to be toggled via quickly
+ * tapping screen 3 times as preferred shortcut.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ UserShortcutType.DEFAULT,
+ UserShortcutType.SOFTWARE,
+ UserShortcutType.HARDWARE,
+ UserShortcutType.TRIPLETAP,
+ })
+ /** Denotes the user shortcut type. */
+ public @interface UserShortcutType {
+ int DEFAULT = 0;
+ int SOFTWARE = 1; // 1 << 0
+ int HARDWARE = 2; // 1 << 1
+ int TRIPLETAP = 4; // 1 << 2
+ }
+
+ /**
* Annotation for different accessibilityService fragment UI type.
*
* {@code LEGACY} for displaying appearance aligned with sdk version Q accessibility service
@@ -395,19 +437,63 @@
}
private void onTargetDeleted(AdapterView<?> parent, View view, int position, long id) {
- // TODO(b/146967898): disable service when deleting the target and the target only have
- // last one shortcut item, only remove it from shortcut list otherwise.
- if ((mShortcutType == ACCESSIBILITY_BUTTON) && (mTargets.get(position).mFragmentType
- != AccessibilityServiceFragmentType.LEGACY)) {
- mTargets.remove(position);
- mTargetAdapter.notifyDataSetChanged();
+ final AccessibilityButtonTarget target = mTargets.get(position);
+ final ComponentName targetComponentName =
+ ComponentName.unflattenFromString(target.getId());
+
+ switch (target.getFragmentType()) {
+ case AccessibilityServiceFragmentType.INVISIBLE:
+ onInvisibleTargetDeleted(targetComponentName);
+ break;
+ case AccessibilityServiceFragmentType.INTUITIVE:
+ onIntuitiveTargetDeleted(targetComponentName);
+ break;
+ case AccessibilityServiceFragmentType.LEGACY:
+ case AccessibilityServiceFragmentType.BOUNCE:
+ // Do nothing
+ break;
+ default:
+ throw new IllegalStateException("Unexpected fragment type");
}
+ mTargets.remove(position);
+ mTargetAdapter.notifyDataSetChanged();
+
if (mTargets.isEmpty()) {
mAlertDialog.dismiss();
}
}
+ private void onInvisibleTargetDeleted(ComponentName componentName) {
+ if (mShortcutType == ACCESSIBILITY_BUTTON) {
+ optOutValueFromSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName);
+
+ if (!hasValueInSettings(this,
+ ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName)) {
+ setAccessibilityServiceState(this, componentName, /* enabled= */ false);
+ }
+ } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+ optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName);
+
+ if (!hasValueInSettings(this,
+ ACCESSIBILITY_BUTTON_USER_TYPE, componentName)) {
+ setAccessibilityServiceState(this, componentName, /* enabled= */ false);
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
+ }
+ }
+
+ private void onIntuitiveTargetDeleted(ComponentName componentName) {
+ if (mShortcutType == ACCESSIBILITY_BUTTON) {
+ optOutValueFromSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName);
+ } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+ optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName);
+ } else {
+ throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
+ }
+ }
+
private void onCancelButtonClicked() {
mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
mTargetAdapter.notifyDataSetChanged();
@@ -437,4 +523,166 @@
mAlertDialog.getListView().setOnItemClickListener(
isEditMenuMode ? this::onTargetDeleted : this::onTargetSelected);
}
+
+ /**
+ * @return the set of enabled accessibility services for {@param userId}. If there are no
+ * services, it returns the unmodifiable {@link Collections#emptySet()}.
+ */
+ private Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) {
+ final String enabledServicesSetting = Settings.Secure.getStringForUser(
+ context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userId);
+ if (TextUtils.isEmpty(enabledServicesSetting)) {
+ return Collections.emptySet();
+ }
+
+ final Set<ComponentName> enabledServices = new HashSet<>();
+ final TextUtils.StringSplitter colonSplitter =
+ new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR);
+ colonSplitter.setString(enabledServicesSetting);
+
+ for (String componentNameString : colonSplitter) {
+ final ComponentName enabledService = ComponentName.unflattenFromString(
+ componentNameString);
+ if (enabledService != null) {
+ enabledServices.add(enabledService);
+ }
+ }
+
+ return enabledServices;
+ }
+
+ /**
+ * Changes an accessibility component's state.
+ */
+ private void setAccessibilityServiceState(Context context, ComponentName componentName,
+ boolean enabled) {
+ setAccessibilityServiceState(context, componentName, enabled, UserHandle.myUserId());
+ }
+
+ /**
+ * Changes an accessibility component's state for {@param userId}.
+ */
+ private void setAccessibilityServiceState(Context context, ComponentName componentName,
+ boolean enabled, int userId) {
+ Set<ComponentName> enabledServices = getEnabledServicesFromSettings(
+ context, userId);
+
+ if (enabledServices.isEmpty()) {
+ enabledServices = new ArraySet<>(/* capacity= */ 1);
+ }
+
+ if (enabled) {
+ enabledServices.add(componentName);
+ } else {
+ enabledServices.remove(componentName);
+ }
+
+ final StringBuilder enabledServicesBuilder = new StringBuilder();
+ for (ComponentName enabledService : enabledServices) {
+ enabledServicesBuilder.append(enabledService.flattenToString());
+ enabledServicesBuilder.append(
+ SERVICES_SEPARATOR);
+ }
+
+ final int enabledServicesBuilderLength = enabledServicesBuilder.length();
+ if (enabledServicesBuilderLength > 0) {
+ enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
+ }
+
+ Settings.Secure.putStringForUser(context.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ enabledServicesBuilder.toString(), userId);
+ }
+
+ /**
+ * Opts out component name into colon-separated {@code shortcutType} key's string in Settings.
+ *
+ * @param context The current context.
+ * @param shortcutType The preferred shortcut type user selected.
+ * @param componentName The component name that need to be opted out from Settings.
+ */
+ private void optOutValueFromSettings(
+ Context context, int shortcutType, ComponentName componentName) {
+ final StringJoiner joiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR));
+ final String targetsKey = convertToKey(shortcutType);
+ final String targetsValue = Settings.Secure.getString(context.getContentResolver(),
+ targetsKey);
+
+ if (TextUtils.isEmpty(targetsValue)) {
+ return;
+ }
+
+ sStringColonSplitter.setString(targetsValue);
+ while (sStringColonSplitter.hasNext()) {
+ final String name = sStringColonSplitter.next();
+ if (TextUtils.isEmpty(name) || (componentName.flattenToString()).equals(name)) {
+ continue;
+ }
+ joiner.add(name);
+ }
+
+ Settings.Secure.putString(context.getContentResolver(), targetsKey, joiner.toString());
+ }
+
+ /**
+ * Returns if component name existed in Settings.
+ *
+ * @param context The current context.
+ * @param shortcutType The preferred shortcut type user selected.
+ * @param componentName The component name that need to be checked existed in Settings.
+ * @return {@code true} if componentName existed in Settings.
+ */
+ private boolean hasValueInSettings(Context context, @UserShortcutType int shortcutType,
+ @NonNull ComponentName componentName) {
+ final String targetKey = convertToKey(shortcutType);
+ final String targetString = Settings.Secure.getString(context.getContentResolver(),
+ targetKey);
+
+ if (TextUtils.isEmpty(targetString)) {
+ return false;
+ }
+
+ sStringColonSplitter.setString(targetString);
+ while (sStringColonSplitter.hasNext()) {
+ final String name = sStringColonSplitter.next();
+ if ((componentName.flattenToString()).equals(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Converts {@link UserShortcutType} to key in Settings.
+ *
+ * @param type The shortcut type.
+ * @return Mapping key in Settings.
+ */
+ private String convertToKey(@UserShortcutType int type) {
+ switch (type) {
+ case UserShortcutType.SOFTWARE:
+ return Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT;
+ case UserShortcutType.HARDWARE:
+ return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+ case UserShortcutType.TRIPLETAP:
+ return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported user shortcut type: " + type);
+ }
+ }
+
+ private static @UserShortcutType int convertToUserType(@ShortcutType int type) {
+ switch (type) {
+ case ACCESSIBILITY_BUTTON:
+ return UserShortcutType.SOFTWARE;
+ case ACCESSIBILITY_SHORTCUT_KEY:
+ return UserShortcutType.HARDWARE;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported shortcut type:" + type);
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl
index d94294f..74bfb96 100644
--- a/core/java/com/android/internal/app/ISoundTriggerService.aidl
+++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl
@@ -61,10 +61,6 @@
int setParameter(in ParcelUuid soundModelId, in ModelParams modelParam,
int value);
- /**
- * @throws UnsupportedOperationException if hal or model do not support this API.
- * @throws IllegalArgumentException if invalid model handle or parameter is passed.
- */
int getParameter(in ParcelUuid soundModelId, in ModelParams modelParam);
@nullable SoundTrigger.ModelParamRange queryParameter(in ParcelUuid soundModelId,
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index f462f5d..be2d1d6 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -26,6 +26,7 @@
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractionSessionListener;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.ModelParams;
import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
@@ -91,6 +92,49 @@
*/
int stopRecognition(in IVoiceInteractionService service, int keyphraseId,
in IRecognitionStatusCallback callback);
+ /**
+ * Set a model specific ModelParams with the given value. This
+ * parameter will keep its value for the duration the model is loaded regardless of starting and
+ * stopping recognition. Once the model is unloaded, the value will be lost.
+ * queryParameter should be checked first before calling this method.
+ *
+ * @param service The current VoiceInteractionService.
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param modelParam ModelParams
+ * @param value Value to set
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+ * if API is not supported by HAL
+ */
+ int setParameter(in IVoiceInteractionService service, int keyphraseId,
+ in ModelParams modelParam, int value);
+ /**
+ * Get a model specific ModelParams. This parameter will keep its value
+ * for the duration the model is loaded regardless of starting and stopping recognition.
+ * Once the model is unloaded, the value will be lost. If the value is not set, a default
+ * value is returned. See ModelParams for parameter default values.
+ * queryParameter should be checked first before calling this method.
+ *
+ * @param service The current VoiceInteractionService.
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param modelParam ModelParams
+ * @return value of parameter
+ */
+ int getParameter(in IVoiceInteractionService service, int keyphraseId,
+ in ModelParams modelParam);
+ /**
+ * Determine if parameter control is supported for the given model handle.
+ * This method should be checked prior to calling setParameter or getParameter.
+ *
+ * @param service The current VoiceInteractionService.
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param modelParam ModelParams
+ * @return supported range of parameter, null if not supported
+ */
+ @nullable SoundTrigger.ModelParamRange queryParameter(in IVoiceInteractionService service,
+ int keyphraseId, in ModelParams modelParam);
/**
* @return the component name for the currently active voice interaction service
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 5bc96d8..408a7a8 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -126,7 +126,9 @@
* @param reference an object reference
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
+ * @deprecated - use {@link java.util.Objects.requireNonNull} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public static @NonNull <T> T checkNotNull(final T reference) {
if (reference == null) {
@@ -144,7 +146,9 @@
* be converted to a string using {@link String#valueOf(Object)}
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
+ * @deprecated - use {@link java.util.Objects.requireNonNull} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) {
if (reference == null) {
@@ -154,26 +158,6 @@
}
/**
- * Ensures that an object reference passed as a parameter to the calling
- * method is not null.
- *
- * @param reference an object reference
- * @param messageTemplate a printf-style message template to use if the check fails; will
- * be converted to a string using {@link String#format(String, Object...)}
- * @param messageArgs arguments for {@code messageTemplate}
- * @return the non-null reference that was validated
- * @throws NullPointerException if {@code reference} is null
- */
- public static @NonNull <T> T checkNotNull(final T reference,
- final String messageTemplate,
- final Object... messageArgs) {
- if (reference == null) {
- throw new NullPointerException(String.format(messageTemplate, messageArgs));
- }
- return reference;
- }
-
- /**
* Ensures the truth of an expression involving the state of the calling
* instance, but not involving any parameters to the calling method.
*
diff --git a/core/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto
index 557075c..2de5b7f 100644
--- a/core/proto/android/service/graphicsstats.proto
+++ b/core/proto/android/service/graphicsstats.proto
@@ -32,6 +32,10 @@
}
message GraphicsStatsProto {
+ enum PipelineType {
+ GL = 0;
+ VULKAN = 1;
+ }
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// The package name of the app
@@ -54,6 +58,9 @@
// The gpu frame time histogram for the package
repeated GraphicsStatsHistogramBucketProto gpu_histogram = 7;
+
+ // HWUI renders pipeline type: GL or Vulkan
+ optional PipelineType pipeline = 8;
}
message GraphicsStatsJankSummaryProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5aa0a2d..d887032 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -370,6 +370,7 @@
<protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
<protected-broadcast android:name="android.net.wifi.LINK_CONFIGURATION_CHANGED" />
<protected-broadcast android:name="android.net.wifi.CONFIGURED_NETWORKS_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.action.NETWORK_SETTINGS_RESET" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_ICON" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST" />
@@ -1654,6 +1655,7 @@
<!-- Allows holder to request bluetooth/wifi scan bypassing global "use location" setting and
location permissions.
<p>Not for use by third-party or privileged applications.
+ @SystemApi
@hide
-->
<permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION"
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index 68b9b00..f4709ff 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -33,14 +33,15 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.support.test.uiautomator.UiDevice;
import android.test.InstrumentationTestCase;
import android.util.Log;
-import libcore.io.Streams;
-
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.MockWebServer;
+import libcore.io.Streams;
+
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
@@ -63,6 +64,7 @@
private static final String TAG = "DownloadManagerBaseTest";
protected DownloadManager mDownloadManager = null;
private MockWebServer mServer = null;
+ private UiDevice mUiDevice = null;
protected String mFileType = "text/plain";
protected Context mContext = null;
protected MultipleDownloadsCompletedReceiver mReceiver = null;
@@ -234,6 +236,7 @@
@Override
public void setUp() throws Exception {
mContext = getInstrumentation().getContext();
+ mUiDevice = UiDevice.getInstance(getInstrumentation());
mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
mServer = new MockWebServer();
mServer.play();
@@ -512,7 +515,7 @@
Log.i(LOG_TAG, "Setting WiFi State to: " + enable);
WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
- manager.setWifiEnabled(enable);
+ mUiDevice.executeShellCommand("svc wifi " + (enable ? "enable" : "disable"));
String timeoutMessage = "Timed out waiting for Wifi to be "
+ (enable ? "enabled!" : "disabled!");
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
index c6c0b46..b13bcd1 100644
--- a/core/tests/coretests/src/android/net/NetworkKeyTest.java
+++ b/core/tests/coretests/src/android/net/NetworkKeyTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.net.wifi.ScanResult;
@@ -107,7 +108,7 @@
@Test
public void createFromScanResult_nullSsid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.BSSID = VALID_BSSID;
assertNull(NetworkKey.createFromScanResult(scanResult));
@@ -115,7 +116,7 @@
@Test
public void createFromScanResult_emptySsid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = "";
scanResult.BSSID = VALID_BSSID;
@@ -124,7 +125,7 @@
@Test
public void createFromScanResult_noneSsid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = WifiManager.UNKNOWN_SSID;
scanResult.BSSID = VALID_BSSID;
@@ -133,7 +134,7 @@
@Test
public void createFromScanResult_nullBssid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = VALID_UNQUOTED_SSID;
assertNull(NetworkKey.createFromScanResult(scanResult));
@@ -141,7 +142,7 @@
@Test
public void createFromScanResult_emptyBssid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = VALID_UNQUOTED_SSID;
scanResult.BSSID = "";
@@ -150,7 +151,7 @@
@Test
public void createFromScanResult_invalidBssid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = VALID_UNQUOTED_SSID;
scanResult.BSSID = INVALID_BSSID;
@@ -159,7 +160,7 @@
@Test
public void createFromScanResult_validSsid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = VALID_UNQUOTED_SSID;
scanResult.BSSID = VALID_BSSID;
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 0e19ca8..d0fd92a 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -90,12 +90,12 @@
mImeConsumer.onWindowFocusGained();
mImeConsumer.applyImeVisibility(true);
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test if setVisibility can hide IME
mImeConsumer.applyImeVisibility(false);
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
}
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 179929f..fa61a0a 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -16,11 +16,11 @@
package android.view;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowInsets.Type.systemBars;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -39,8 +39,6 @@
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.test.InsetsModeSession;
-import androidx.test.runner.AndroidJUnit4;
-
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -52,6 +50,8 @@
import java.util.List;
+import androidx.test.runner.AndroidJUnit4;
+
/**
* Tests for {@link InsetsAnimationControlImpl}.
*
@@ -116,7 +116,7 @@
mController = new InsetsAnimationControlImpl(controls,
new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
mMockController, 10 /* durationMs */,
- false /* fade */);
+ false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN);
}
@Test
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index a89fc1e..1db96b1 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -68,6 +68,7 @@
private InsetsController mController;
private SurfaceSession mSession = new SurfaceSession();
private SurfaceControl mLeash;
+ private ViewRootImpl mViewRoot;
@Before
public void setup() {
@@ -77,13 +78,13 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
Context context = InstrumentationRegistry.getTargetContext();
// cannot mock ViewRootImpl since it's final.
- ViewRootImpl viewRootImpl = new ViewRootImpl(context, context.getDisplay());
+ mViewRoot = new ViewRootImpl(context, context.getDisplay());
try {
- viewRootImpl.setView(new TextView(context), new LayoutParams(), null);
+ mViewRoot.setView(new TextView(context), new LayoutParams(), null);
} catch (BadTokenException e) {
// activity isn't running, we will ignore BadTokenException.
}
- mController = new InsetsController(viewRootImpl);
+ mController = new InsetsController(mViewRoot);
final Rect rect = new Rect(5, 5, 5, 5);
mController.calculateInsets(
false,
@@ -117,16 +118,22 @@
@Test
public void testControlsRevoked_duringAnim() {
- InsetsSourceControl control =
- new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
- mController.onControlsChanged(new InsetsSourceControl[] { control });
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ InsetsSourceControl control =
+ new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
- WindowInsetsAnimationControlListener mockListener =
- mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */, mockListener);
- verify(mockListener).onReady(any(), anyInt());
- mController.onControlsChanged(new InsetsSourceControl[0]);
- verify(mockListener).onCancelled();
+ WindowInsetsAnimationControlListener mockListener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */,
+ mockListener);
+
+ // Ready gets deferred until next predraw
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+ verify(mockListener).onReady(any(), anyInt());
+ mController.onControlsChanged(new InsetsSourceControl[0]);
+ verify(mockListener).onCancelled();
+ });
}
@Test
@@ -154,16 +161,16 @@
mController.show(Type.all());
// quickly jump to final state by cancelling it.
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.applyImeVisibility(false /* setVisible */);
mController.hide(Type.all());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -180,10 +187,10 @@
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
mController.applyImeVisibility(true);
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.applyImeVisibility(false);
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -201,16 +208,16 @@
// test show select types.
mController.show(types);
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test hide all
mController.hide(types);
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -227,29 +234,29 @@
// test show select types.
mController.show(types);
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test hide all
mController.hide(Type.all());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test single show
mController.show(Type.navigationBars());
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test single hide
mController.hide(Type.navigationBars());
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -267,31 +274,31 @@
mController.show(Type.navigationBars());
mController.show(Type.systemBars());
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.hide(Type.navigationBars());
mController.hide(Type.systemBars());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
int types = Type.navigationBars() | Type.systemBars();
// show two at a time and hide one by one.
mController.show(types);
mController.hide(Type.navigationBars());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.hide(Type.systemBars());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -309,15 +316,15 @@
mController.show(types);
mController.hide(Type.navigationBars());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.hide(Type.systemBars());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -336,12 +343,16 @@
ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor =
ArgumentCaptor.forClass(WindowInsetsAnimationController.class);
+
+ // Ready gets deferred until next predraw
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+
verify(mockListener).onReady(controllerCaptor.capture(), anyInt());
controllerCaptor.getValue().finish(false /* shown */);
});
waitUntilNextFrame();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isVisible());
+ assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 7af833b..492c036 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -96,7 +96,7 @@
@Test
public void testHide() {
mConsumer.hide();
- assertFalse("Consumer should not be visible", mConsumer.isVisible());
+ assertFalse("Consumer should not be visible", mConsumer.isRequestedVisible());
verify(mSpyInsetsSource).setVisible(eq(false));
}
@@ -106,7 +106,7 @@
// Insets source starts out visible
mConsumer.hide();
mConsumer.show();
- assertTrue("Consumer should be visible", mConsumer.isVisible());
+ assertTrue("Consumer should be visible", mConsumer.isRequestedVisible());
verify(mSpyInsetsSource).setVisible(eq(false));
verify(mSpyInsetsSource).setVisible(eq(true));
}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index d7f50ba..e3b08bb 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -108,5 +108,30 @@
assertEquals(Insets.of(0, 100, 0, 0), insets);
}
+ @Test
+ public void testCalculateVisibleInsets_default() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ Insets insets = mSource.calculateVisibleInsets(new Rect(100, 0, 500, 500));
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateVisibleInsets_override() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.setVisibleFrame(new Rect(0, 0, 500, 200));
+ Insets insets = mSource.calculateVisibleInsets(new Rect(100, 0, 500, 500));
+ assertEquals(Insets.of(0, 200, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateVisibleInsets_invisible() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.setVisibleFrame(new Rect(0, 0, 500, 200));
+ mSource.setVisible(false);
+ Insets insets = mSource.calculateVisibleInsets(new Rect(100, 0, 500, 500));
+ assertEquals(Insets.of(0, 0, 0, 0), insets);
+ }
+
+
// Parcel and equals already tested via InsetsStateTest
}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index fa2ffcca..4b76fee 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -18,12 +18,14 @@
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_TOP;
+import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static org.junit.Assert.assertEquals;
@@ -209,6 +211,42 @@
assertFalse(InsetsState.getDefaultVisibility(ITYPE_IME));
}
+ @Test
+ public void testCalculateVisibleInsets() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+
+ // Make sure bottom gestures are ignored
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
+ Rect visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_PAN);
+ assertEquals(new Rect(0, 100, 0, 100), visibleInsets);
+ }
+ }
+
+ @Test
+ public void testCalculateVisibleInsets_adjustNothing() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+
+ // Make sure bottom gestures are ignored
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
+ Rect visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_NOTHING);
+ assertEquals(new Rect(0, 100, 0, 0), visibleInsets);
+ }
+ }
+
private void assertEqualsAndHashCode() {
assertEquals(mState, mState2);
assertEquals(mState.hashCode(), mState2.hashCode());
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index f497db2..89c2374 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -226,6 +226,61 @@
}
@Test
+ public void testEditor_onTouchEvent_quickTapAfterDrag() throws Throwable {
+ String text = "Hi world!";
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Editor editor = tv.getEditorForTesting();
+
+ // Simulate a tap-and-drag gesture.
+ long event1Time = 1001;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 5f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event2Time = 1002;
+ MotionEvent event2 = moveEvent(event1Time, event2Time, 50f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
+ assertTrue(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event3Time = 1003;
+ MotionEvent event3 = moveEvent(event1Time, event3Time, 100f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
+ assertTrue(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event4Time = 2004;
+ MotionEvent event4 = upEvent(event1Time, event4Time, 100f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ // Simulate a quick tap after the drag, near the location where the drag ended.
+ long event5Time = 2005;
+ MotionEvent event5 = downEvent(event5Time, event5Time, 90f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5));
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event6Time = 2006;
+ MotionEvent event6 = upEvent(event5Time, event6Time, 90f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event6));
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ // Simulate another quick tap in the same location; now selection should be triggered.
+ long event7Time = 2007;
+ MotionEvent event7 = downEvent(event7Time, event7Time, 90f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event7));
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertTrue(editor.getSelectionController().isCursorBeingModified());
+ }
+
+ @Test
public void testEditor_onTouchEvent_cursorDrag() throws Throwable {
String text = "testEditor_onTouchEvent_cursorDrag";
onView(withId(R.id.textview)).perform(replaceText(text));
@@ -237,29 +292,25 @@
// Simulate a tap-and-drag gesture. This should trigger a cursor drag.
long event1Time = 1001;
MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event1));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
long event2Time = 1002;
MotionEvent event2 = moveEvent(event1Time, event2Time, 21f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event2));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
long event3Time = 1003;
- MotionEvent event3 = moveEvent(event3Time, event3Time, 120f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event3));
- mInstrumentation.waitForIdleSync();
+ MotionEvent event3 = moveEvent(event1Time, event3Time, 120f, 30f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
assertTrue(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
long event4Time = 1004;
- MotionEvent event4 = upEvent(event3Time, event4Time, 120f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event4));
- mInstrumentation.waitForIdleSync();
+ MotionEvent event4 = upEvent(event1Time, event4Time, 120f, 30f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
}
@@ -276,36 +327,31 @@
// Simulate a double-tap followed by a drag. This should trigger a selection drag.
long event1Time = 1001;
MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event1));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
long event2Time = 1002;
MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event2));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
long event3Time = 1003;
MotionEvent event3 = downEvent(event3Time, event3Time, 20f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event3));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertTrue(editor.getSelectionController().isCursorBeingModified());
long event4Time = 1004;
MotionEvent event4 = moveEvent(event3Time, event4Time, 120f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event4));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertTrue(editor.getSelectionController().isCursorBeingModified());
long event5Time = 1005;
MotionEvent event5 = upEvent(event3Time, event5Time, 120f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event5));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
}
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index 6adb1b8..215d0b8 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -120,6 +120,60 @@
}
@Test
+ public void testUpdate_doubleTap_delayAfterFirstDownEvent() throws Exception {
+ // Simulate an ACTION_DOWN event.
+ long event1Time = 1000;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mTouchState.update(event1, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+
+ // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
+ long event2Time = 1000 + ViewConfiguration.getDoubleTapTimeout() + 1;
+ MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+ mTouchState.update(event2, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false);
+
+ // Generate an ACTION_DOWN event whose time is within the double-tap timeout when
+ // calculated from the last ACTION_UP event time. Even though the time between the last up
+ // and this down event is within the double-tap timeout, this should not be considered a
+ // double-tap (since the first down event had a longer delay).
+ long event3Time = event2Time + 1;
+ MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f);
+ mTouchState.update(event3, mConfig);
+ assertSingleTap(mTouchState, 22f, 33f, 20f, 30f, false);
+ }
+
+ @Test
+ public void testUpdate_quickTapAfterDrag() throws Exception {
+ // Simulate an ACTION_DOWN event.
+ long event1Time = 1000;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mTouchState.update(event1, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+
+ // Simulate an ACTION_MOVE event.
+ long event2Time = 1001;
+ MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f);
+ mTouchState.update(event2, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0, true);
+
+ // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
+ long event3Time = 5000;
+ MotionEvent event3 = upEvent(event1Time, event3Time, 200f, 31f);
+ mTouchState.update(event3, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 200f, 31f, false);
+
+ // Generate an ACTION_DOWN event whose time is within the double-tap timeout when
+ // calculated from the last ACTION_UP event time. Even though the time between the last up
+ // and this down event is within the double-tap timeout, this should not be considered a
+ // double-tap (since the first down event had a longer delay).
+ long event4Time = event3Time + 1;
+ MotionEvent event4 = downEvent(event4Time, event4Time, 200f, 31f);
+ mTouchState.update(event4, mConfig);
+ assertSingleTap(mTouchState, 200f, 31f, 200f, 31f, false);
+ }
+
+ @Test
public void testUpdate_tripleClick_mouse() throws Exception {
// Simulate an ACTION_DOWN event.
long event1Time = 1000;
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d945fc4..a7f8cc4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -215,6 +215,7 @@
android: {
srcs: [
+ "pipeline/skia/ATraceMemoryDump.cpp",
"pipeline/skia/GLFunctorDrawable.cpp",
"pipeline/skia/LayerDrawable.cpp",
"pipeline/skia/ShaderCache.cpp",
@@ -244,7 +245,6 @@
"DeviceInfo.cpp",
"FrameInfo.cpp",
"FrameInfoVisualizer.cpp",
- "GpuMemoryTracker.cpp",
"HardwareBitmapUploader.cpp",
"HWUIProperties.sysprop",
"JankTracker.cpp",
@@ -325,7 +325,6 @@
"tests/unit/DamageAccumulatorTests.cpp",
"tests/unit/DeferredLayerUpdaterTests.cpp",
"tests/unit/FatVectorTests.cpp",
- "tests/unit/GpuMemoryTrackerTests.cpp",
"tests/unit/GraphicsStatsServiceTests.cpp",
"tests/unit/LayerUpdateQueueTests.cpp",
"tests/unit/LinearAllocatorTests.cpp",
diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp
deleted file mode 100644
index a9a7af8..0000000
--- a/libs/hwui/GpuMemoryTracker.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You 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.
- */
-
-#include "utils/StringUtils.h"
-
-#include <GpuMemoryTracker.h>
-#include <cutils/compiler.h>
-#include <utils/Trace.h>
-#include <array>
-#include <sstream>
-#include <unordered_set>
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-pthread_t gGpuThread = 0;
-
-#define NUM_TYPES static_cast<int>(GpuObjectType::TypeCount)
-
-const char* TYPE_NAMES[] = {
- "Texture", "OffscreenBuffer", "Layer",
-};
-
-struct TypeStats {
- int totalSize = 0;
- int count = 0;
-};
-
-static std::array<TypeStats, NUM_TYPES> gObjectStats;
-static std::unordered_set<GpuMemoryTracker*> gObjectSet;
-
-void GpuMemoryTracker::notifySizeChanged(int newSize) {
- int delta = newSize - mSize;
- mSize = newSize;
- gObjectStats[static_cast<int>(mType)].totalSize += delta;
-}
-
-void GpuMemoryTracker::startTrackingObject() {
- auto result = gObjectSet.insert(this);
- LOG_ALWAYS_FATAL_IF(!result.second,
- "startTrackingObject() on %p failed, already being tracked!", this);
- gObjectStats[static_cast<int>(mType)].count++;
-}
-
-void GpuMemoryTracker::stopTrackingObject() {
- size_t removed = gObjectSet.erase(this);
- LOG_ALWAYS_FATAL_IF(removed != 1, "stopTrackingObject removed %zd, is %p not being tracked?",
- removed, this);
- gObjectStats[static_cast<int>(mType)].count--;
-}
-
-void GpuMemoryTracker::onGpuContextCreated() {
- LOG_ALWAYS_FATAL_IF(gGpuThread != 0,
- "We already have a gpu thread? "
- "current = %lu, gpu thread = %lu",
- pthread_self(), gGpuThread);
- gGpuThread = pthread_self();
-}
-
-void GpuMemoryTracker::onGpuContextDestroyed() {
- gGpuThread = 0;
- if (CC_UNLIKELY(gObjectSet.size() > 0)) {
- std::stringstream os;
- dump(os);
- ALOGE("%s", os.str().c_str());
- LOG_ALWAYS_FATAL("Leaked %zd GPU objects!", gObjectSet.size());
- }
-}
-
-void GpuMemoryTracker::dump() {
- std::stringstream strout;
- dump(strout);
- ALOGD("%s", strout.str().c_str());
-}
-
-void GpuMemoryTracker::dump(std::ostream& stream) {
- for (int type = 0; type < NUM_TYPES; type++) {
- const TypeStats& stats = gObjectStats[type];
- stream << TYPE_NAMES[type];
- stream << " is using " << SizePrinter{stats.totalSize};
- stream << ", count = " << stats.count;
- stream << std::endl;
- }
-}
-
-int GpuMemoryTracker::getInstanceCount(GpuObjectType type) {
- return gObjectStats[static_cast<int>(type)].count;
-}
-
-int GpuMemoryTracker::getTotalSize(GpuObjectType type) {
- return gObjectStats[static_cast<int>(type)].totalSize;
-}
-
-void GpuMemoryTracker::onFrameCompleted() {
- if (ATRACE_ENABLED()) {
- char buf[128];
- for (int type = 0; type < NUM_TYPES; type++) {
- snprintf(buf, 128, "hwui_%s", TYPE_NAMES[type]);
- const TypeStats& stats = gObjectStats[type];
- ATRACE_INT(buf, stats.totalSize);
- snprintf(buf, 128, "hwui_%s_count", TYPE_NAMES[type]);
- ATRACE_INT(buf, stats.count);
- }
- }
-}
-
-} // namespace uirenderer
-} // namespace android;
diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h
deleted file mode 100644
index de3ca99..0000000
--- a/libs/hwui/GpuMemoryTracker.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You 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.
- */
-#pragma once
-
-#include <pthread.h>
-#include <ostream>
-
-#include <log/log.h>
-
-namespace android {
-namespace uirenderer {
-
-extern pthread_t gGpuThread;
-
-#define ASSERT_GPU_THREAD() \
- LOG_ALWAYS_FATAL_IF(!pthread_equal(gGpuThread, pthread_self()), \
- "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \
- "!= gpu thread %lu", \
- this, static_cast<int>(mType), mSize, pthread_self(), gGpuThread)
-
-enum class GpuObjectType {
- Texture = 0,
- OffscreenBuffer,
- Layer,
-
- TypeCount,
-};
-
-class GpuMemoryTracker {
-public:
- GpuObjectType objectType() { return mType; }
- int objectSize() { return mSize; }
-
- static void onGpuContextCreated();
- static void onGpuContextDestroyed();
- static void dump();
- static void dump(std::ostream& stream);
- static int getInstanceCount(GpuObjectType type);
- static int getTotalSize(GpuObjectType type);
- static void onFrameCompleted();
-
-protected:
- explicit GpuMemoryTracker(GpuObjectType type) : mType(type) {
- ASSERT_GPU_THREAD();
- startTrackingObject();
- }
-
- ~GpuMemoryTracker() {
- notifySizeChanged(0);
- stopTrackingObject();
- }
-
- void notifySizeChanged(int newSize);
-
-private:
- void startTrackingObject();
- void stopTrackingObject();
-
- int mSize = 0;
- GpuObjectType mType;
-};
-
-} // namespace uirenderer
-} // namespace android;
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index 7921662..a8e36e3 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -15,6 +15,7 @@
*/
#include "ProfileData.h"
+#include "Properties.h"
#include <cinttypes>
@@ -102,6 +103,7 @@
mGPUFrameCounts[i] >>= divider;
mGPUFrameCounts[i] += other.mGPUFrameCounts[i];
}
+ mPipelineType = other.mPipelineType;
}
void ProfileData::dump(int fd) const {
@@ -157,6 +159,7 @@
mTotalFrameCount = 0;
mJankFrameCount = 0;
mStatStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mPipelineType = Properties::getRenderPipelineType();
}
void ProfileData::reportFrame(int64_t duration) {
diff --git a/libs/hwui/ProfileData.h b/libs/hwui/ProfileData.h
index ccbffc6..dd3ba66 100644
--- a/libs/hwui/ProfileData.h
+++ b/libs/hwui/ProfileData.h
@@ -16,6 +16,7 @@
#pragma once
+#include "Properties.h"
#include "utils/Macros.h"
#include <utils/Timers.h>
@@ -65,6 +66,7 @@
uint32_t jankFrameCount() const { return mJankFrameCount; }
nsecs_t statsStartTime() const { return mStatStartTime; }
uint32_t jankTypeCount(JankType type) const { return mJankTypeCounts[static_cast<int>(type)]; }
+ RenderPipelineType pipelineType() const { return mPipelineType; }
struct HistogramEntry {
uint32_t renderTimeMs;
@@ -103,6 +105,9 @@
uint32_t mTotalFrameCount;
uint32_t mJankFrameCount;
nsecs_t mStatStartTime;
+
+ // true if HWUI renders with Vulkan pipeline
+ RenderPipelineType mPipelineType;
};
// For testing
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
new file mode 100644
index 0000000..2c78b02
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+
+#include "ATraceMemoryDump.h"
+
+#include <utils/Trace.h>
+
+#include <cstring>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+// When purgeable is INVALID_TIME it won't be logged at all.
+#define INVALID_TIME -1
+
+/**
+ * Skia invokes the following SkTraceMemoryDump functions:
+ * 1. dumpNumericValue (dumpName, units="bytes", valueName="size")
+ * 2. dumpStringValue (dumpName, valueName="type") [optional -> for example CPU memory does not
+ * invoke dumpStringValue]
+ * 3. dumpNumericValue (dumpName, units="bytes", valueName="purgeable_size") [optional]
+ * 4. setMemoryBacking(dumpName, backingType) [optional -> for example Vulkan GPU resources do not
+ * invoke setMemoryBacking]
+ *
+ * ATraceMemoryDump calculates memory category first by looking at the "type" string passed to
+ * dumpStringValue and then by looking at "backingType" passed to setMemoryBacking.
+ * Only GPU Texture memory is tracked separately and everything else is grouped as one
+ * "GPU Memory" category.
+ */
+static std::unordered_map<const char*, const char*> sResourceMap = {
+ {"malloc", "Graphics CPU Memory"}, // taken from setMemoryBacking(backingType)
+ {"gl_texture", "Graphics Texture Memory"}, // taken from setMemoryBacking(backingType)
+ {"Texture",
+ "Graphics Texture Memory"}, // taken from dumpStringValue(value, valueName="type")
+ // Uncomment categories below to split "GPU Memory" into more brackets for debugging.
+ /*{"vk_buffer", "vk_buffer"},
+ {"gl_renderbuffer", "gl_renderbuffer"},
+ {"gl_buffer", "gl_buffer"},
+ {"RenderTarget", "RenderTarget"},
+ {"Stencil", "Stencil"},
+ {"Path Data", "Path Data"},
+ {"Buffer Object", "Buffer Object"},
+ {"Surface", "Surface"},*/
+};
+
+ATraceMemoryDump::ATraceMemoryDump() {
+ mLastDumpName.reserve(100);
+ mCategory.reserve(100);
+}
+
+void ATraceMemoryDump::dumpNumericValue(const char* dumpName, const char* valueName,
+ const char* units, uint64_t value) {
+ if (!strcmp(units, "bytes")) {
+ recordAndResetCountersIfNeeded(dumpName);
+ if (!strcmp(valueName, "size")) {
+ mLastDumpValue = value;
+ } else if (!strcmp(valueName, "purgeable_size")) {
+ mLastPurgeableDumpValue = value;
+ }
+ }
+}
+
+void ATraceMemoryDump::dumpStringValue(const char* dumpName, const char* valueName,
+ const char* value) {
+ if (!strcmp(valueName, "type")) {
+ recordAndResetCountersIfNeeded(dumpName);
+ auto categoryIt = sResourceMap.find(value);
+ if (categoryIt != sResourceMap.end()) {
+ mCategory = categoryIt->second;
+ }
+ }
+}
+
+void ATraceMemoryDump::setMemoryBacking(const char* dumpName, const char* backingType,
+ const char* backingObjectId) {
+ recordAndResetCountersIfNeeded(dumpName);
+ auto categoryIt = sResourceMap.find(backingType);
+ if (categoryIt != sResourceMap.end()) {
+ mCategory = categoryIt->second;
+ }
+}
+
+/**
+ * startFrame is invoked before dumping anything. It resets counters from the previous frame.
+ * This is important, because if there is no new data for a given category trace would assume
+ * usage has not changed (instead of reporting 0).
+ */
+void ATraceMemoryDump::startFrame() {
+ resetCurrentCounter("");
+ for (auto& it : mCurrentValues) {
+ // Once a category is observed in at least one frame, it is always reported in subsequent
+ // frames (even if it is 0). Not logging a category to ATRACE would mean its value has not
+ // changed since the previous frame, which is not what we want.
+ it.second.time = 0;
+ // If purgeableTime is INVALID_TIME, then logTraces won't log it at all.
+ if (it.second.purgeableTime != INVALID_TIME) {
+ it.second.purgeableTime = 0;
+ }
+ }
+}
+
+/**
+ * logTraces reads from mCurrentValues and logs the counters with ATRACE.
+ */
+void ATraceMemoryDump::logTraces() {
+ // Accumulate data from last dumpName
+ recordAndResetCountersIfNeeded("");
+ for (auto& it : mCurrentValues) {
+ ATRACE_INT64(it.first.c_str(), it.second.time);
+ if (it.second.purgeableTime != INVALID_TIME) {
+ ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableTime);
+ }
+ }
+}
+
+/**
+ * recordAndResetCountersIfNeeded reads memory usage from mLastDumpValue/mLastPurgeableDumpValue and
+ * accumulates in mCurrentValues[category]. It makes provision to create a new category and track
+ * purgeable memory only if there is at least one observation.
+ * recordAndResetCountersIfNeeded won't do anything until all the information for a given dumpName
+ * is received.
+ */
+void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) {
+ if (!mLastDumpName.compare(dumpName)) {
+ // Still waiting for more data for current dumpName.
+ return;
+ }
+
+ // First invocation will have an empty mLastDumpName.
+ if (!mLastDumpName.empty()) {
+ // A new dumpName observed -> store the data already collected.
+ auto memoryCounter = mCurrentValues.find(mCategory);
+ if (memoryCounter != mCurrentValues.end()) {
+ memoryCounter->second.time += mLastDumpValue;
+ if (mLastPurgeableDumpValue != INVALID_TIME) {
+ if (memoryCounter->second.purgeableTime == INVALID_TIME) {
+ memoryCounter->second.purgeableTime = mLastPurgeableDumpValue;
+ } else {
+ memoryCounter->second.purgeableTime += mLastPurgeableDumpValue;
+ }
+ }
+ } else {
+ mCurrentValues[mCategory] = {mLastDumpValue, mLastPurgeableDumpValue};
+ }
+ }
+
+ // Reset counters and default category for the newly observed "dumpName".
+ resetCurrentCounter(dumpName);
+}
+
+void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) {
+ mLastDumpValue = 0;
+ mLastPurgeableDumpValue = INVALID_TIME;
+ mLastDumpName = dumpName;
+ // Categories not listed in sResourceMap are reported as "GPU memory"
+ mCategory = "GPU Memory";
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h
new file mode 100644
index 0000000..aa5c401
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <SkString.h>
+#include <SkTraceMemoryDump.h>
+
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class ATraceMemoryDump : public SkTraceMemoryDump {
+public:
+ ATraceMemoryDump();
+ ~ATraceMemoryDump() override {}
+
+ void dumpNumericValue(const char* dumpName, const char* valueName, const char* units,
+ uint64_t value) override;
+
+ void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override;
+
+ LevelOfDetail getRequestedDetails() const override {
+ return SkTraceMemoryDump::kLight_LevelOfDetail;
+ }
+
+ bool shouldDumpWrappedObjects() const override { return false; }
+
+ void setMemoryBacking(const char* dumpName, const char* backingType,
+ const char* backingObjectId) override;
+
+ void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}
+
+ void startFrame();
+
+ void logTraces();
+
+private:
+ std::string mLastDumpName;
+
+ uint64_t mLastDumpValue;
+
+ uint64_t mLastPurgeableDumpValue;
+
+ std::string mCategory;
+
+ struct TraceValue {
+ uint64_t time;
+ uint64_t purgeableTime;
+ };
+
+ // keys are define in sResourceMap
+ std::unordered_map<std::string, TraceValue> mCurrentValues;
+
+ void recordAndResetCountersIfNeeded(const char* dumpName);
+
+ void resetCurrentCounter(const char* dumpName);
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
\ No newline at end of file
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 11dc013..35a885f 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -457,7 +457,10 @@
const Rect& contentDrawBounds, SkCanvas* canvas,
const SkMatrix& preTransform) {
SkAutoCanvasRestore saver(canvas, true);
- canvas->androidFramework_setDeviceClipRestriction(preTransform.mapRect(clip).roundOut());
+ auto clipRestriction = preTransform.mapRect(clip).roundOut();
+ canvas->androidFramework_setDeviceClipRestriction(clipRestriction);
+ canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction",
+ nullptr);
canvas->concat(preTransform);
// STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
diff --git a/libs/hwui/protos/graphicsstats.proto b/libs/hwui/protos/graphicsstats.proto
index 0cd5c62..dd5676c 100644
--- a/libs/hwui/protos/graphicsstats.proto
+++ b/libs/hwui/protos/graphicsstats.proto
@@ -29,6 +29,11 @@
}
message GraphicsStatsProto {
+ enum PipelineType {
+ GL = 0;
+ VULKAN = 1;
+ }
+
// The package name of the app
optional string package_name = 1;
@@ -49,6 +54,9 @@
// The gpu frame time histogram for the package
repeated GraphicsStatsHistogramBucketProto gpu_histogram = 7;
+
+ // HWUI renders pipeline type: GL or Vulkan
+ optional PipelineType pipeline = 8;
}
message GraphicsStatsJankSummaryProto {
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index fad9440..7e8c96d 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -16,7 +16,6 @@
#include "renderstate/RenderState.h"
#include "renderthread/RenderThread.h"
-#include "GpuMemoryTracker.h"
namespace android {
namespace uirenderer {
@@ -25,15 +24,10 @@
mThreadId = pthread_self();
}
-void RenderState::onContextCreated() {
- GpuMemoryTracker::onGpuContextCreated();
-}
-
void RenderState::onContextDestroyed() {
for(auto callback : mContextCallbacks) {
callback->onContextDestroyed();
}
- GpuMemoryTracker::onGpuContextDestroyed();
}
void RenderState::postDecStrong(VirtualLightRefBase* object) {
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index ff5d02f..e08d32a 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -62,7 +62,6 @@
~RenderState() {}
// Context notifications are only to be triggered by renderthread::RenderThread
- void onContextCreated();
void onContextDestroyed();
std::set<IGpuContextCallback*> mContextCallbacks;
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index eaed46c..d177855 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -20,10 +20,12 @@
#include "Layer.h"
#include "Properties.h"
#include "RenderThread.h"
+#include "pipeline/skia/ATraceMemoryDump.h"
#include "pipeline/skia/ShaderCache.h"
#include "pipeline/skia/SkiaMemoryTracer.h"
#include "renderstate/RenderState.h"
#include "thread/CommonPool.h"
+#include <utils/Trace.h>
#include <GrContextOptions.h>
#include <SkExecutor.h>
@@ -184,6 +186,18 @@
gpuTracer.logTotals(log);
}
+void CacheManager::onFrameCompleted() {
+ if (ATRACE_ENABLED()) {
+ static skiapipeline::ATraceMemoryDump tracer;
+ tracer.startFrame();
+ SkGraphics::DumpMemoryStatistics(&tracer);
+ if (mGrContext) {
+ mGrContext->dumpMemoryStatistics(&tracer);
+ }
+ tracer.logTraces();
+ }
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 968251e..b009cc4 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -50,6 +50,7 @@
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
+ void onFrameCompleted();
private:
friend class RenderThread;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 8490221..5993e17 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -16,7 +16,6 @@
#include "CanvasContext.h"
-#include <GpuMemoryTracker.h>
#include <apex/window.h>
#include <fcntl.h>
#include <strings.h>
@@ -558,7 +557,7 @@
mJankTracker.finishGpuDraw(*forthBehind);
}
- GpuMemoryTracker::onFrameCompleted();
+ mRenderThread.cacheManager().onFrameCompleted();
}
// Called by choreographer to do an RT-driven animation
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index d78f641..cae3e3b 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -270,7 +270,6 @@
}
mGrContext = std::move(context);
if (mGrContext) {
- mRenderState->onContextCreated();
DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize());
}
}
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 12c5b83..c418617 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -16,24 +16,28 @@
#include "GraphicsStatsService.h"
-#include "JankTracker.h"
-#include "protos/graphicsstats.pb.h"
-
-#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
-#include <log/log.h>
-
#include <errno.h>
#include <fcntl.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <inttypes.h>
+#include <log/log.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "JankTracker.h"
+#include "protos/graphicsstats.pb.h"
+
namespace android {
namespace uirenderer {
using namespace google::protobuf;
+using namespace uirenderer::protos;
constexpr int32_t sCurrentFileVersion = 1;
constexpr int32_t sHeaderSize = 4;
@@ -42,9 +46,9 @@
constexpr int sHistogramSize = ProfileData::HistogramSize();
constexpr int sGPUHistogramSize = ProfileData::GPUHistogramSize();
-static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto,
- const std::string& package, int64_t versionCode,
- int64_t startTime, int64_t endTime, const ProfileData* data);
+static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::string& package,
+ int64_t versionCode, int64_t startTime, int64_t endTime,
+ const ProfileData* data);
static void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int outFd);
class FileDescriptor {
@@ -57,7 +61,7 @@
}
}
bool valid() { return mFd != -1; }
- operator int() { return mFd; } // NOLINT(google-explicit-constructor)
+ operator int() { return mFd; } // NOLINT(google-explicit-constructor)
private:
int mFd;
@@ -167,6 +171,8 @@
}
proto->set_package_name(package);
proto->set_version_code(versionCode);
+ proto->set_pipeline(data->pipelineType() == RenderPipelineType::SkiaGL ?
+ GraphicsStatsProto_PipelineType_GL : GraphicsStatsProto_PipelineType_VULKAN);
auto summary = proto->mutable_summary();
summary->set_total_frames(summary->total_frames() + data->totalFrameCount());
summary->set_janky_frames(summary->janky_frames() + data->jankFrameCount());
@@ -179,8 +185,8 @@
summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
data->jankTypeCount(kSlowSync));
summary->set_slow_draw_count(summary->slow_draw_count() + data->jankTypeCount(kSlowRT));
- summary->set_missed_deadline_count(summary->missed_deadline_count()
- + data->jankTypeCount(kMissedDeadline));
+ summary->set_missed_deadline_count(summary->missed_deadline_count() +
+ data->jankTypeCount(kMissedDeadline));
bool creatingHistogram = false;
if (proto->histogram_size() == 0) {
@@ -365,17 +371,69 @@
class GraphicsStatsService::Dump {
public:
- Dump(int outFd, DumpType type) : mFd(outFd), mType(type) {}
+ Dump(int outFd, DumpType type) : mFd(outFd), mType(type) {
+ if (mFd == -1 && mType == DumpType::Protobuf) {
+ mType = DumpType::ProtobufStatsd;
+ }
+ }
int fd() { return mFd; }
DumpType type() { return mType; }
protos::GraphicsStatsServiceDumpProto& proto() { return mProto; }
+ void mergeStat(const protos::GraphicsStatsProto& stat);
+ void updateProto();
private:
+ // use package name and app version for a key
+ typedef std::pair<std::string, int64_t> DumpKey;
+
+ std::map<DumpKey, protos::GraphicsStatsProto> mStats;
int mFd;
DumpType mType;
protos::GraphicsStatsServiceDumpProto mProto;
};
+void GraphicsStatsService::Dump::mergeStat(const protos::GraphicsStatsProto& stat) {
+ auto dumpKey = std::make_pair(stat.package_name(), stat.version_code());
+ auto findIt = mStats.find(dumpKey);
+ if (findIt == mStats.end()) {
+ mStats[dumpKey] = stat;
+ } else {
+ auto summary = findIt->second.mutable_summary();
+ summary->set_total_frames(summary->total_frames() + stat.summary().total_frames());
+ summary->set_janky_frames(summary->janky_frames() + stat.summary().janky_frames());
+ summary->set_missed_vsync_count(summary->missed_vsync_count() +
+ stat.summary().missed_vsync_count());
+ summary->set_high_input_latency_count(summary->high_input_latency_count() +
+ stat.summary().high_input_latency_count());
+ summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() +
+ stat.summary().slow_ui_thread_count());
+ summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
+ stat.summary().slow_bitmap_upload_count());
+ summary->set_slow_draw_count(summary->slow_draw_count() + stat.summary().slow_draw_count());
+ summary->set_missed_deadline_count(summary->missed_deadline_count() +
+ stat.summary().missed_deadline_count());
+ for (int bucketIndex = 0; bucketIndex < findIt->second.histogram_size(); bucketIndex++) {
+ auto bucket = findIt->second.mutable_histogram(bucketIndex);
+ bucket->set_frame_count(bucket->frame_count() +
+ stat.histogram(bucketIndex).frame_count());
+ }
+ for (int bucketIndex = 0; bucketIndex < findIt->second.gpu_histogram_size();
+ bucketIndex++) {
+ auto bucket = findIt->second.mutable_gpu_histogram(bucketIndex);
+ bucket->set_frame_count(bucket->frame_count() +
+ stat.gpu_histogram(bucketIndex).frame_count());
+ }
+ findIt->second.set_stats_start(std::min(findIt->second.stats_start(), stat.stats_start()));
+ findIt->second.set_stats_end(std::max(findIt->second.stats_end(), stat.stats_end()));
+ }
+}
+
+void GraphicsStatsService::Dump::updateProto() {
+ for (auto& stat : mStats) {
+ mProto.add_stats()->CopyFrom(stat.second);
+ }
+}
+
GraphicsStatsService::Dump* GraphicsStatsService::createDump(int outFd, DumpType type) {
return new Dump(outFd, type);
}
@@ -396,8 +454,9 @@
path.empty() ? "<empty>" : path.c_str(), data);
return;
}
-
- if (dump->type() == DumpType::Protobuf) {
+ if (dump->type() == DumpType::ProtobufStatsd) {
+ dump->mergeStat(statsProto);
+ } else if (dump->type() == DumpType::Protobuf) {
dump->proto().add_stats()->CopyFrom(statsProto);
} else {
dumpAsTextToFd(&statsProto, dump->fd());
@@ -409,7 +468,9 @@
if (!parseFromFile(path, &statsProto)) {
return;
}
- if (dump->type() == DumpType::Protobuf) {
+ if (dump->type() == DumpType::ProtobufStatsd) {
+ dump->mergeStat(statsProto);
+ } else if (dump->type() == DumpType::Protobuf) {
dump->proto().add_stats()->CopyFrom(statsProto);
} else {
dumpAsTextToFd(&statsProto, dump->fd());
@@ -424,5 +485,79 @@
delete dump;
}
+class MemOutputStreamLite : public io::ZeroCopyOutputStream {
+public:
+ explicit MemOutputStreamLite() : mCopyAdapter(), mImpl(&mCopyAdapter) {}
+ virtual ~MemOutputStreamLite() {}
+
+ virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); }
+
+ virtual void BackUp(int count) override { mImpl.BackUp(count); }
+
+ virtual int64 ByteCount() const override { return mImpl.ByteCount(); }
+
+ bool Flush() { return mImpl.Flush(); }
+
+ void copyData(const DumpMemoryFn& reader, void* param1, void* param2) {
+ int bufferOffset = 0;
+ int totalSize = mCopyAdapter.mBuffersSize - mCopyAdapter.mCurrentBufferUnusedSize;
+ int totalDataLeft = totalSize;
+ for (auto& it : mCopyAdapter.mBuffers) {
+ int bufferSize = std::min(totalDataLeft, (int)it.size()); // last buffer is not full
+ reader(it.data(), bufferOffset, bufferSize, totalSize, param1, param2);
+ bufferOffset += bufferSize;
+ totalDataLeft -= bufferSize;
+ }
+ }
+
+private:
+ struct MemAdapter : public io::CopyingOutputStream {
+ // Data is stored in an array of buffers.
+ // JNI SetByteArrayRegion assembles data in one continuous Java byte[] buffer.
+ std::vector<std::vector<unsigned char>> mBuffers;
+ int mBuffersSize = 0; // total bytes allocated in mBuffers
+ int mCurrentBufferUnusedSize = 0; // unused bytes in the last buffer mBuffers.back()
+ unsigned char* mCurrentBuffer = nullptr; // pointer to next free byte in mBuffers.back()
+
+ explicit MemAdapter() {}
+ virtual ~MemAdapter() {}
+
+ virtual bool Write(const void* buffer, int size) override {
+ while (size > 0) {
+ if (0 == mCurrentBufferUnusedSize) {
+ mCurrentBufferUnusedSize =
+ std::max(size, mBuffersSize ? 2 * mBuffersSize : 10000);
+ mBuffers.emplace_back();
+ mBuffers.back().resize(mCurrentBufferUnusedSize);
+ mCurrentBuffer = mBuffers.back().data();
+ mBuffersSize += mCurrentBufferUnusedSize;
+ }
+ int dataMoved = std::min(mCurrentBufferUnusedSize, size);
+ memcpy(mCurrentBuffer, buffer, dataMoved);
+ mCurrentBufferUnusedSize -= dataMoved;
+ mCurrentBuffer += dataMoved;
+ buffer = reinterpret_cast<const unsigned char*>(buffer) + dataMoved;
+ size -= dataMoved;
+ }
+ return true;
+ }
+ };
+
+ MemOutputStreamLite::MemAdapter mCopyAdapter;
+ io::CopyingOutputStreamAdaptor mImpl;
+};
+
+void GraphicsStatsService::finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1,
+ void* param2) {
+ MemOutputStreamLite stream;
+ dump->updateProto();
+ bool success = dump->proto().SerializeToZeroCopyStream(&stream) && stream.Flush();
+ delete dump;
+ if (!success) {
+ return;
+ }
+ stream.copyData(reader, param1, param2);
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h
index 389f599..4bed9633 100644
--- a/libs/hwui/service/GraphicsStatsService.h
+++ b/libs/hwui/service/GraphicsStatsService.h
@@ -27,6 +27,9 @@
class GraphicsStatsProto;
}
+typedef void (*DumpMemoryFn)(void* buffer, int bufferOffset, int bufferSize, int totalSize,
+ void* param1, void* param2);
+
/*
* The exported entry points used by GraphicsStatsService.java in f/b/services/core
*
@@ -40,6 +43,7 @@
enum class DumpType {
Text,
Protobuf,
+ ProtobufStatsd,
};
ANDROID_API static void saveBuffer(const std::string& path, const std::string& package,
@@ -52,6 +56,8 @@
int64_t startTime, int64_t endTime, const ProfileData* data);
ANDROID_API static void addToDump(Dump* dump, const std::string& path);
ANDROID_API static void finishDump(Dump* dump);
+ ANDROID_API static void finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1,
+ void* param2);
// Visible for testing
static bool parseFromFile(const std::string& path, protos::GraphicsStatsProto* output);
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
deleted file mode 100644
index dac888c..0000000
--- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You 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.
- */
-
-#include <GpuMemoryTracker.h>
-#include <gtest/gtest.h>
-
-#include "renderthread/EglManager.h"
-#include "renderthread/RenderThread.h"
-#include "tests/common/TestUtils.h"
-
-#include <utils/StrongPointer.h>
-
-using namespace android;
-using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-
-class TestGPUObject : public GpuMemoryTracker {
-public:
- TestGPUObject() : GpuMemoryTracker(GpuObjectType::Texture) {}
-
- void changeSize(int newSize) { notifySizeChanged(newSize); }
-};
-
-// Other tests may have created a renderthread and EGL context.
-// This will destroy the EGLContext on RenderThread if it exists so that the
-// current thread can spoof being a GPU thread
-static void destroyEglContext() {
- if (TestUtils::isRenderThreadRunning()) {
- TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); });
- }
-}
-
-TEST(GpuMemoryTracker, sizeCheck) {
- destroyEglContext();
-
- GpuMemoryTracker::onGpuContextCreated();
- ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
- ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
- {
- TestGPUObject myObj;
- ASSERT_EQ(1, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
- myObj.changeSize(500);
- ASSERT_EQ(500, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
- myObj.changeSize(1000);
- ASSERT_EQ(1000, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
- myObj.changeSize(300);
- ASSERT_EQ(300, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
- }
- ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
- ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
- GpuMemoryTracker::onGpuContextDestroyed();
-}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index aff7257..aece39d 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -940,16 +940,15 @@
/**
* Called when a media key event is dispatched through the media session service. The
* session token can be {@link null} if the framework has sent the media key event to the
- * media button receiver to revive the media app's playback.
- *
- * the session is dead when , but the framework sent
+ * media button receiver to revive the media app's playback after the corresponding session
+ * is released.
*
* @param event Dispatched media key event.
* @param packageName Package
* @param sessionToken The media session's token. Can be {@code null}.
*/
default void onMediaKeyEventDispatched(@NonNull KeyEvent event, @NonNull String packageName,
- @NonNull MediaSession.Token sessionToken) { }
+ @Nullable MediaSession.Token sessionToken) { }
}
/**
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 1c38301..938ffcd 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -428,8 +428,7 @@
*/
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
public int setParameter(@Nullable UUID soundModelId,
- @ModelParams int modelParam, int value)
- throws UnsupportedOperationException, IllegalArgumentException {
+ @ModelParams int modelParam, int value) {
try {
return mSoundTriggerService.setParameter(new ParcelUuid(soundModelId), modelParam,
value);
@@ -449,15 +448,10 @@
* @param soundModelId UUID of model to get parameter
* @param modelParam {@link ModelParams}
* @return value of parameter
- * @throws UnsupportedOperationException if hal or model do not support this API.
- * {@link SoundTriggerManager#queryParameter} should be checked first.
- * @throws IllegalArgumentException if invalid model handle or parameter is passed.
- * {@link SoundTriggerManager#queryParameter} should be checked first.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
public int getParameter(@NonNull UUID soundModelId,
- @ModelParams int modelParam)
- throws UnsupportedOperationException, IllegalArgumentException {
+ @ModelParams int modelParam) {
try {
return mSoundTriggerService.getParameter(new ParcelUuid(soundModelId), modelParam);
} catch (RemoteException e) {
@@ -479,8 +473,7 @@
public ModelParamRange queryParameter(@Nullable UUID soundModelId,
@ModelParams int modelParam) {
try {
- return mSoundTriggerService.queryParameter(new ParcelUuid(soundModelId),
- modelParam);
+ return mSoundTriggerService.queryParameter(new ParcelUuid(soundModelId), modelParam);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
index 1a3b402..909f1a00 100644
--- a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
+++ b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
@@ -30,6 +30,14 @@
* Unique implementation ID. The UUID must change with each version of
the engine implementation */
String uuid;
+ /**
+ * String naming the architecture used for running the supported models.
+ * (eg. a platform running models on a DSP could implement this string to convey the DSP
+ * architecture used)
+ * This property is supported for soundtrigger HAL v2.3 and above.
+ * If running a previous version, the string will be empty.
+ */
+ String supportedModelArch;
/** Maximum number of concurrent sound models loaded */
int maxSoundModels;
/** Maximum number of key phrases */
diff --git a/media/java/android/media/soundtrigger_middleware/Status.aidl b/media/java/android/media/soundtrigger_middleware/Status.aidl
index d8f9d8f..5d082e2 100644
--- a/media/java/android/media/soundtrigger_middleware/Status.aidl
+++ b/media/java/android/media/soundtrigger_middleware/Status.aidl
@@ -26,4 +26,6 @@
RESOURCE_CONTENTION = 1,
/** Operation is not supported in this implementation. This is a permanent condition. */
OPERATION_NOT_SUPPORTED = 2,
+ /** Temporary lack of permission. */
+ TEMPORARY_PERMISSION_DENIED = 3,
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index ee67613..536a061 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -143,6 +143,10 @@
"libutils",
],
+ header_libs: [
+ "libstagefright_foundation_headers",
+ ],
+
export_include_dirs: ["."],
cflags: [
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index a2bd210..a784e04 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
@@ -148,17 +151,18 @@
}
public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.connect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- // Downgrade priority as user is disconnecting the headset.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService == null) {
+ return false;
}
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -182,12 +186,12 @@
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -197,11 +201,11 @@
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
boolean isA2dpPlaying() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index bc03c34..8ca5a74 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
@@ -116,18 +119,15 @@
if (mService == null) {
return false;
}
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
if (mService == null) {
return false;
}
- // Downgrade priority as user is disconnecting the headset.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -141,12 +141,12 @@
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -156,11 +156,11 @@
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 560cb3b..d65b5da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -112,18 +115,15 @@
if (mService == null) {
return false;
}
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
if (mService == null) {
return false;
}
- // Downgrade priority as user is disconnecting the headset.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -165,12 +165,12 @@
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -180,11 +180,11 @@
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index b4b55f3..9f1af66 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -146,17 +149,18 @@
}
public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.connect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- // Downgrade priority as user is disconnecting the hearing aid.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService == null) {
+ return false;
}
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -180,12 +184,12 @@
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -195,11 +199,11 @@
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index a372e23..678f2e3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -126,7 +129,7 @@
if (mService == null) {
return false;
}
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
@Override
@@ -134,11 +137,8 @@
if (mService == null) {
return false;
}
- // Downgrade priority as user is disconnecting the headset.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
@Override
@@ -154,13 +154,13 @@
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
@Override
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -171,11 +171,11 @@
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 975a1e6..588083e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -99,13 +102,17 @@
}
public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.connect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.disconnect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -119,12 +126,12 @@
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) != CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -132,11 +139,11 @@
public void setPreferred(BluetoothDevice device, boolean preferred) {
if (mService == null) return;
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 95139a1..7d121aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -115,18 +118,15 @@
if (mService == null) {
return false;
}
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
if (mService == null) {
return false;
}
- // Downgrade priority as user is disconnecting.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -140,12 +140,12 @@
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -155,11 +155,11 @@
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 31a0eea..a96a4e7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -16,6 +16,8 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -119,10 +121,8 @@
if (mService == null) {
return false;
}
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -136,12 +136,12 @@
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -155,7 +155,7 @@
mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 4ea0df6..56267fc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -129,7 +132,7 @@
return false;
}
Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
@@ -137,7 +140,7 @@
if (mService == null) {
return false;
}
- return mService.disconnect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -151,12 +154,12 @@
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -166,11 +169,11 @@
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index 3f920a8..f7c0bf5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -16,6 +16,8 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -96,8 +98,10 @@
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.disconnect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 0ca4d61..3022c5b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -112,17 +115,15 @@
if (mService == null) {
return false;
}
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
if (mService == null) {
return false;
}
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -136,12 +137,12 @@
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -151,11 +152,11 @@
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
index 976445e..ccb6646 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -67,13 +70,13 @@
@Test
public void connect_shouldConnectBluetoothA2dpSink() {
mProfile.connect(mBluetoothDevice);
- verify(mService).connect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
}
@Test
public void disconnect_shouldDisconnectBluetoothA2dpSink() {
mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
index 69c020d..9180760 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -67,13 +70,13 @@
@Test
public void connect_shouldConnectBluetoothHeadsetClient() {
mProfile.connect(mBluetoothDevice);
- verify(mService).connect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
}
@Test
public void disconnect_shouldDisconnectBluetoothHeadsetClient() {
mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
index 6f66709..1425c38 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -67,13 +70,13 @@
@Test
public void connect_shouldConnectBluetoothMapClient() {
mProfile.connect(mBluetoothDevice);
- verify(mService).connect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
}
@Test
public void disconnect_shouldDisconnectBluetoothMapClient() {
mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
index b21ec9c3..15f560b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -67,13 +70,13 @@
@Test
public void connect_shouldConnectBluetoothPbapClient() {
mProfile.connect(mBluetoothDevice);
- verify(mService).connect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
}
@Test
public void disconnect_shouldDisconnectBluetoothPbapClient() {
mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
index ec88034..4f978a8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -66,13 +69,13 @@
@Test
public void connect_shouldConnectBluetoothSap() {
mProfile.connect(mBluetoothDevice);
- verify(mService).connect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
}
@Test
public void disconnect_shouldDisconnectBluetoothSap() {
mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
}
@Test
diff --git a/packages/SystemUI/docs/broadcasts.md b/packages/SystemUI/docs/broadcasts.md
index 56a637f..28657f2 100644
--- a/packages/SystemUI/docs/broadcasts.md
+++ b/packages/SystemUI/docs/broadcasts.md
@@ -42,24 +42,29 @@
```kotlin
/**
- * Register a receiver for broadcast with the dispatcher
- *
- * @param receiver A receiver to dispatch the [Intent]
- * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
- * It will only take into account actions and categories for filtering. It must
- * have at least one action.
- * @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the
- * main handler. Pass `null` to use the default.
- * @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the current user.
- * @throws IllegalArgumentException if the filter has other constraints that are not actions or
- * categories or the filter has no actions.
- */
+ * Register a receiver for broadcast with the dispatcher
+ *
+ * @param receiver A receiver to dispatch the [Intent]
+ * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
+ * It will only take into account actions and categories for filtering. It must
+ * have at least one action.
+ * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
+ * executor in the main thread (default).
+ * @param user A user handle to determine which broadcast should be dispatched to this receiver.
+ * By default, it is the current user.
+ * @throws IllegalArgumentException if the filter has other constraints that are not actions or
+ * categories or the filter has no actions.
+ */
@JvmOverloads
-fun registerReceiver(BroadcastReceiver, IntentFilter, Handler? = mainHandler, UserHandle = context.user)
+fun registerReceiver(
+ BroadcastReceiver,
+ IntentFilter,
+ Executor? = context.mainExecutor,
+ UserHandle = context.user
+) {
```
-All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a `UserHandle` with the default `Handler`, pass `null` for the `Handler`.
+All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a `UserHandle` with the default `Executor`, pass `null` for the `Executor`.
In the same way as with `Context`, subscribing the same `BroadcastReceiver` for the same user using different filters will result on two subscriptions, not in replacing the filter.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 431862f..a58e3d7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1638,7 +1638,7 @@
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- broadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mHandler);
+ broadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, mHandler);
final IntentFilter allUserFilter = new IntentFilter();
allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
@@ -1649,8 +1649,8 @@
allUserFilter.addAction(ACTION_USER_UNLOCKED);
allUserFilter.addAction(ACTION_USER_STOPPED);
allUserFilter.addAction(ACTION_USER_REMOVED);
- broadcastDispatcher.registerReceiver(mBroadcastAllReceiver, allUserFilter, mHandler,
- UserHandle.ALL);
+ broadcastDispatcher.registerReceiverWithHandler(mBroadcastAllReceiver, allUserFilter,
+ mHandler, UserHandle.ALL);
mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
try {
diff --git a/packages/SystemUI/src/com/android/systemui/DumpController.kt b/packages/SystemUI/src/com/android/systemui/DumpController.kt
index 8c7075b..f14c4cd 100644
--- a/packages/SystemUI/src/com/android/systemui/DumpController.kt
+++ b/packages/SystemUI/src/com/android/systemui/DumpController.kt
@@ -30,6 +30,14 @@
/**
* Controller that allows any [Dumpable] to subscribe and be dumped along with other SystemUI
* dependencies.
+ *
+ * To dump a specific dumpable on-demand:
+ *
+ * ```
+ * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService dependency DumpController <tag1>,<tag2>,<tag3>
+ * ```
+ *
+ * Where tag1, tag2, etc. are the tags of the dumpables you want to dump.
*/
@Singleton
class DumpController @Inject constructor() : Dumpable {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 0e736dc..c533755 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -266,7 +266,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter, mHandler);
+ mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 8cb0cc5..cedf7c3 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -20,6 +20,7 @@
import android.content.Context
import android.content.IntentFilter
import android.os.Handler
+import android.os.HandlerExecutor
import android.os.Looper
import android.os.Message
import android.os.UserHandle
@@ -32,13 +33,14 @@
import com.android.systemui.dagger.qualifiers.Main
import java.io.FileDescriptor
import java.io.PrintWriter
+import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
data class ReceiverData(
val receiver: BroadcastReceiver,
val filter: IntentFilter,
- val handler: Handler,
+ val executor: Executor,
val user: UserHandle
)
@@ -76,8 +78,33 @@
* @param filter A filter to determine what broadcasts should be dispatched to this receiver.
* It will only take into account actions and categories for filtering. It must
* have at least one action.
- * @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the
- * main handler. Pass `null` to use the default.
+ * @param handler A handler to dispatch [BroadcastReceiver.onReceive].
+ * @param user A user handle to determine which broadcast should be dispatched to this receiver.
+ * By default, it is the current user.
+ * @throws IllegalArgumentException if the filter has other constraints that are not actions or
+ * categories or the filter has no actions.
+ */
+ @Deprecated(message = "Replacing Handler for Executor in SystemUI",
+ replaceWith = ReplaceWith("registerReceiver(receiver, filter, executor, user)"))
+ @JvmOverloads
+ fun registerReceiverWithHandler(
+ receiver: BroadcastReceiver,
+ filter: IntentFilter,
+ handler: Handler,
+ user: UserHandle = context.user
+ ) {
+ registerReceiver(receiver, filter, HandlerExecutor(handler), user)
+ }
+
+ /**
+ * Register a receiver for broadcast with the dispatcher
+ *
+ * @param receiver A receiver to dispatch the [Intent]
+ * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
+ * It will only take into account actions and categories for filtering. It must
+ * have at least one action.
+ * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
+ * executor in the main thread (default).
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
* By default, it is the current user.
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
@@ -85,15 +112,15 @@
*/
@JvmOverloads
fun registerReceiver(
- receiver: BroadcastReceiver,
- filter: IntentFilter,
- handler: Handler? = mainHandler,
- user: UserHandle = context.user
+ receiver: BroadcastReceiver,
+ filter: IntentFilter,
+ executor: Executor? = context.mainExecutor,
+ user: UserHandle = context.user
) {
checkFilter(filter)
this.handler
.obtainMessage(MSG_ADD_RECEIVER,
- ReceiverData(receiver, filter, handler ?: mainHandler, user))
+ ReceiverData(receiver, filter, executor ?: context.mainExecutor, user))
.sendToTarget()
}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
index b2942bb..0c631aa 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
@@ -193,7 +193,7 @@
it.filter.hasAction(intent.action) &&
it.filter.matchCategories(intent.categories) == null }
?.forEach {
- it.handler.post {
+ it.executor.execute {
if (DEBUG) Log.w(TAG,
"[$index] Dispatching ${intent.action} to ${it.receiver}")
it.receiver.pendingResult = pendingResult
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index c9faf69..4e88726 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -91,7 +91,7 @@
if (mDebuggable) {
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_AOD_BRIGHTNESS);
- mBroadcastDispatcher.registerReceiver(this, filter, handler, UserHandle.ALL);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter, handler, UserHandle.ALL);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 59ac329..48750fa 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -233,7 +233,7 @@
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(this, filter, mHandler);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index bbff117..ad79cad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -322,7 +322,7 @@
filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
try {
mUserReceiverRegistered.set(true);
- mBroadcastDispatcher.registerReceiver(this, filter, mHandler, mUser);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler, mUser);
} catch (Exception ex) {
mUserReceiverRegistered.set(false);
Log.e(TAG, "Could not register unlock receiver", ex);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
index 077d260..9599d77 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -98,7 +98,8 @@
if (!mReceiverRegistered) {
mCurrentUserId = ActivityManager.getCurrentUser();
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(this, filter, null, UserHandle.ALL);
+ mBroadcastDispatcher.registerReceiver(this, filter, null,
+ UserHandle.ALL);
mReceiverRegistered = true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index ec1efa5..b960b42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -24,7 +24,6 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import java.util.Objects;
/**
* Represents a set of grouped notifications. The final notification list is usually a mix of
@@ -58,22 +57,15 @@
@VisibleForTesting
public void setSummary(@Nullable NotificationEntry summary) {
- if (!Objects.equals(mSummary, summary)) {
- mSummary = summary;
- onGroupingUpdated();
- }
+ mSummary = summary;
}
void clearChildren() {
- if (mChildren.size() != 0) {
- mChildren.clear();
- onGroupingUpdated();
- }
+ mChildren.clear();
}
void addChild(NotificationEntry child) {
mChildren.add(child);
- onGroupingUpdated();
}
void sortChildren(Comparator<? super NotificationEntry> c) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index 601b3e0..dc68c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -18,38 +18,20 @@
import android.annotation.Nullable;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.notification.collection.provider.DerivedMember;
-import com.android.systemui.statusbar.notification.collection.provider.IsHighPriorityProvider;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
/**
* Abstract superclass for top-level entries, i.e. things that can appear in the final notification
* list shown to users. In practice, this means either GroupEntries or NotificationEntries.
*/
public abstract class ListEntry {
private final String mKey;
- private final IsHighPriorityProvider mIsHighPriorityProvider = new IsHighPriorityProvider();
- private final List<DerivedMember> mDerivedMemberList = Arrays.asList(mIsHighPriorityProvider);
@Nullable private GroupEntry mParent;
@Nullable private GroupEntry mPreviousParent;
private int mSection;
int mFirstAddedIteration = -1;
- // TODO: (b/145659174) remove groupManager when moving to NewNotifPipeline. Logic
- // replaced in GroupEntry and NotifListBuilderImpl
- private final NotificationGroupManager mGroupManager;
-
ListEntry(String key) {
mKey = key;
-
- // TODO: (b/145659174) remove
- mGroupManager = Dependency.get(NotificationGroupManager.class);
}
public String getKey() {
@@ -68,11 +50,7 @@
}
void setParent(@Nullable GroupEntry parent) {
- if (!Objects.equals(mParent, parent)) {
- invalidateParent();
- mParent = parent;
- onGroupingUpdated();
- }
+ mParent = parent;
}
@Nullable public GroupEntry getPreviousParent() {
@@ -91,58 +69,4 @@
void setSection(int section) {
mSection = section;
}
-
- /**
- * Resets the cached values of DerivedMembers.
- */
- void invalidateDerivedMembers() {
- for (int i = 0; i < mDerivedMemberList.size(); i++) {
- mDerivedMemberList.get(i).invalidate();
- }
- }
-
- /**
- * Whether this notification is shown to the user as a high priority notification: visible on
- * the lock screen/status bar and in the top section in the shade.
- */
- public boolean isHighPriority() {
- return mIsHighPriorityProvider.get(this);
- }
-
- private void invalidateParent() {
- // invalidate our parent (GroupEntry) since DerivedMembers may be dependent on children
- if (getParent() != null) {
- getParent().invalidateDerivedMembers();
- }
-
- // TODO: (b/145659174) remove
- final NotificationEntry notifEntry = getRepresentativeEntry();
- if (notifEntry != null && mGroupManager.isGroupChild(notifEntry.getSbn())) {
- NotificationEntry summary = mGroupManager.getLogicalGroupSummary(notifEntry.getSbn());
- if (summary != null) {
- summary.invalidateDerivedMembers();
- }
- }
- }
-
- void onGroupingUpdated() {
- for (int i = 0; i < mDerivedMemberList.size(); i++) {
- mDerivedMemberList.get(i).onGroupingUpdated();
- }
- invalidateParent();
- }
-
- void onSbnUpdated() {
- for (int i = 0; i < mDerivedMemberList.size(); i++) {
- mDerivedMemberList.get(i).onSbnUpdated();
- }
- invalidateParent();
- }
-
- void onRankingUpdated() {
- for (int i = 0; i < mDerivedMemberList.size(); i++) {
- mDerivedMemberList.get(i).onRankingUpdated();
- }
- invalidateParent();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 28e486d..7301fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -204,11 +204,8 @@
+ " doesn't match existing key " + mKey);
}
- if (!Objects.equals(mSbn, sbn)) {
- mSbn = sbn;
- mBubbleMetadata = mSbn.getNotification().getBubbleMetadata();
- onSbnUpdated();
- }
+ mSbn = sbn;
+ mBubbleMetadata = mSbn.getNotification().getBubbleMetadata();
}
/**
@@ -233,10 +230,7 @@
+ " doesn't match existing key " + mKey);
}
- if (!Objects.equals(mRanking, ranking)) {
- mRanking = ranking;
- onRankingUpdated();
- }
+ mRanking = ranking;
}
/*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 7010943..3bbd722 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.logging.NotifEvent
import com.android.systemui.statusbar.notification.logging.NotifLog
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
@@ -54,7 +55,8 @@
private val notifFilter: NotificationFilter,
private val notifLog: NotifLog,
sectionsFeatureManager: NotificationSectionsFeatureManager,
- private val peopleNotificationIdentifier: PeopleNotificationIdentifier
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
+ private val highPriorityProvider: HighPriorityProvider
) {
var rankingMap: RankingMap? = null
@@ -81,6 +83,9 @@
val aHeadsUp = a.isRowHeadsUp
val bHeadsUp = b.isRowHeadsUp
+ val aIsHighPriority = a.isHighPriority()
+ val bIsHighPriority = b.isHighPriority()
+
when {
usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1
aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1
@@ -90,8 +95,8 @@
aMedia != bMedia -> if (aMedia) -1 else 1
// Upsort PRIORITY_MAX system notifications
aSystemMax != bSystemMax -> if (aSystemMax) -1 else 1
- a.isHighPriority != b.isHighPriority ->
- -1 * a.isHighPriority.compareTo(b.isHighPriority)
+ aIsHighPriority != bIsHighPriority ->
+ -1 * aIsHighPriority.compareTo(bIsHighPriority)
aRank != bRank -> aRank - bRank
else -> nb.notification.`when`.compareTo(na.notification.`when`)
}
@@ -154,7 +159,7 @@
) {
if (usePeopleFiltering && entry.isPeopleNotification()) {
entry.bucket = BUCKET_PEOPLE
- } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority) {
+ } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority()) {
entry.bucket = BUCKET_ALERTING
} else {
entry.bucket = BUCKET_SILENT
@@ -178,10 +183,6 @@
// TODO: notify group manager here?
groupManager.onEntryUpdated(entry, oldSbn)
}
-
- // TODO: (b/145659174) remove after moving to new NotifPipeline
- // (should be able to remove all groupManager code post-migration)
- entry.invalidateDerivedMembers()
}
}
}
@@ -191,6 +192,9 @@
sbn.isPeopleNotification()
private fun StatusBarNotification.isPeopleNotification() =
peopleNotificationIdentifier.isPeopleNotification(this)
+
+ private fun NotificationEntry.isHighPriority() =
+ highPriorityProvider.isHighPriority(this)
}
// Convenience functions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 9312c22..db107f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -43,6 +43,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@@ -62,6 +63,7 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final HighPriorityProvider mHighPriorityProvider;
@Inject
public KeyguardCoordinator(
@@ -71,15 +73,16 @@
NotificationLockscreenUserManager lockscreenUserManager,
BroadcastDispatcher broadcastDispatcher,
StatusBarStateController statusBarStateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ HighPriorityProvider highPriorityProvider) {
mContext = context;
mMainHandler = mainThreadHandler;
mKeyguardStateController = keyguardStateController;
mLockscreenUserManager = lockscreenUserManager;
-
mBroadcastDispatcher = broadcastDispatcher;
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mHighPriorityProvider = highPriorityProvider;
}
@Override
@@ -151,7 +154,7 @@
}
if (NotificationUtils.useNewInterruptionModel(mContext)
&& hideSilentNotificationsOnLockscreen()) {
- return entry.isHighPriority();
+ return mHighPriorityProvider.isHighPriority(entry);
} else {
return entry.getRepresentativeEntry() != null
&& !entry.getRepresentativeEntry().getRanking().isAmbient();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java
deleted file mode 100644
index 815e6f7..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.provider;
-/**
- * Caches a computed value until invalidate() is called
- * @param <Parent> Object used to computeValue
- * @param <Value> type of value to cache until invalidate is called
- */
-public abstract class DerivedMember<Parent, Value> {
- private Value mValue;
- protected abstract Value computeValue(Parent parent);
-
- /**
- * Gets the last cached value, else recomputes the value.
- */
- public Value get(Parent parent) {
- if (mValue == null) {
- mValue = computeValue(parent);
- }
- return mValue;
- }
-
- /**
- * Resets the cached value.
- * Next time "get" is called, the value is recomputed.
- */
- public void invalidate() {
- mValue = null;
- }
-
- /**
- * Called when a NotificationEntry's status bar notification has updated.
- * Derived members can invalidate here.
- */
- public void onSbnUpdated() {}
-
- /**
- * Called when a NotificationEntry's Ranking has updated.
- * Derived members can invalidate here.
- */
- public void onRankingUpdated() {}
-
- /**
- * Called when a ListEntry's grouping information (parent or children) has changed.
- * Derived members can invalidate here.
- */
- public void onGroupingUpdated() {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
new file mode 100644
index 0000000..3cc5e62
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.provider;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Determines whether a notification is considered 'high priority'.
+ *
+ * Notifications that are high priority are visible on the lock screen/status bar and in the top
+ * section in the shade.
+ */
+@Singleton
+public class HighPriorityProvider {
+ private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+
+ @Inject
+ public HighPriorityProvider(PeopleNotificationIdentifier peopleNotificationIdentifier) {
+ mPeopleNotificationIdentifier = peopleNotificationIdentifier;
+ }
+
+ /**
+ * @return true if the ListEntry is high priority, else false
+ *
+ * A NotificationEntry is considered high priority if it:
+ * - has importance greater than or equal to IMPORTANCE_DEFAULT
+ * OR
+ * - their importance has NOT been set to a low priority option by the user AND the
+ * notification fulfills one of the following:
+ * - has a person associated with it
+ * - has a media session associated with it
+ * - has messaging style
+ *
+ * A GroupEntry is considered high priority if its representativeEntry (summary) or children are
+ * high priority
+ */
+ public boolean isHighPriority(ListEntry entry) {
+ if (entry == null) {
+ return false;
+ }
+
+ final NotificationEntry notifEntry = entry.getRepresentativeEntry();
+ return notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
+ || hasHighPriorityCharacteristics(notifEntry)
+ || hasHighPriorityChild(entry);
+ }
+
+
+ private boolean hasHighPriorityChild(ListEntry entry) {
+ if (entry instanceof GroupEntry) {
+ for (NotificationEntry child : ((GroupEntry) entry).getChildren()) {
+ if (isHighPriority(child)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean hasHighPriorityCharacteristics(NotificationEntry entry) {
+ return !hasUserSetImportance(entry)
+ && (isImportantOngoing(entry)
+ || entry.getSbn().getNotification().hasMediaSession()
+ || isPeopleNotification(entry)
+ || isMessagingStyle(entry));
+ }
+
+ private boolean isImportantOngoing(NotificationEntry entry) {
+ return entry.getSbn().getNotification().isForegroundService()
+ && entry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_LOW;
+ }
+
+ private boolean isMessagingStyle(NotificationEntry entry) {
+ return Notification.MessagingStyle.class.equals(
+ entry.getSbn().getNotification().getNotificationStyle());
+ }
+
+ private boolean isPeopleNotification(NotificationEntry entry) {
+ return mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn());
+ }
+
+ private boolean hasUserSetImportance(NotificationEntry entry) {
+ return entry.getRanking().getChannel() != null
+ && entry.getRanking().getChannel().hasUserSetImportance();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java
deleted file mode 100644
index 76e256b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.provider;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.Person;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Whether the ListEntry is shown to the user as a high priority notification: visible on
- * the lock screen/status bar and in the top section in the shade.
- *
- * A NotificationEntry is considered high priority if it:
- * - has importance greater than or equal to IMPORTANCE_DEFAULT
- * OR
- * - their importance has NOT been set to a low priority option by the user AND the notification
- * fulfills one of the following:
- * - has a person associated with it
- * - has a media session associated with it
- * - has messaging style
- *
- * A GroupEntry is considered high priority if its representativeEntry (summary) or children are
- * high priority
- */
-public class IsHighPriorityProvider extends DerivedMember<ListEntry, Boolean> {
- // TODO: (b/145659174) remove groupManager when moving to NewNotifPipeline. Logic
- // replaced in GroupEntry and NotifListBuilderImpl
- private final NotificationGroupManager mGroupManager;
-
-
- public IsHighPriorityProvider() {
- // TODO: (b/145659174) remove
- mGroupManager = Dependency.get(NotificationGroupManager.class);
- }
-
- @Override
- protected Boolean computeValue(ListEntry entry) {
- if (entry == null) {
- return false;
- }
-
- return isHighPriority(entry);
- }
-
- private boolean isHighPriority(ListEntry listEntry) {
- // requires groups have been set (AFTER PipelineState.STATE_TRANSFORMING)
- final NotificationEntry notifEntry = listEntry.getRepresentativeEntry();
- return notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
- || hasHighPriorityCharacteristics(notifEntry)
- || hasHighPriorityChild(listEntry);
-
- }
-
- private boolean hasHighPriorityChild(ListEntry entry) {
- // TODO: (b/145659174) remove
- if (entry instanceof NotificationEntry) {
- NotificationEntry notifEntry = (NotificationEntry) entry;
- if (mGroupManager.isSummaryOfGroup(notifEntry.getSbn())) {
- List<NotificationEntry> logicalChildren =
- mGroupManager.getLogicalChildren(notifEntry.getSbn());
- for (NotificationEntry child : logicalChildren) {
- if (child.isHighPriority()) {
- return true;
- }
- }
- }
- }
-
- if (entry instanceof GroupEntry) {
- for (NotificationEntry child : ((GroupEntry) entry).getChildren()) {
- if (child.isHighPriority()) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean hasHighPriorityCharacteristics(NotificationEntry entry) {
- return !hasUserSetImportance(entry)
- && (isImportantOngoing(entry)
- || entry.getSbn().getNotification().hasMediaSession()
- || hasPerson(entry)
- || isMessagingStyle(entry));
- }
-
- private boolean isImportantOngoing(NotificationEntry entry) {
- return entry.getSbn().getNotification().isForegroundService()
- && entry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_LOW;
- }
-
- private boolean isMessagingStyle(NotificationEntry entry) {
- return Notification.MessagingStyle.class.equals(
- entry.getSbn().getNotification().getNotificationStyle());
- }
-
- private boolean hasPerson(NotificationEntry entry) {
- // TODO: cache favorite and recent contacts to check contact affinity
- Notification notification = entry.getSbn().getNotification();
- ArrayList<Person> people = notification.extras != null
- ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
- : new ArrayList<>();
- return people != null && !people.isEmpty();
- }
-
- private boolean hasUserSetImportance(NotificationEntry entry) {
- return entry.getRanking().getChannel() != null
- && entry.getRanking().getChannel().hasUserSetImportance();
- }
-
- @Override
- public void onSbnUpdated() {
- invalidate();
- }
-
- @Override
- public void onRankingUpdated() {
- invalidate();
- }
-
- @Override
- public void onGroupingUpdated() {
- invalidate();
- }
-}
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 6f2abba..779a224 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
@@ -53,6 +53,7 @@
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -81,6 +82,7 @@
private final Context mContext;
private final VisualStabilityManager mVisualStabilityManager;
private final AccessibilityManager mAccessibilityManager;
+ private final HighPriorityProvider mHighPriorityProvider;
// Dependencies:
private final NotificationLockscreenUserManager mLockscreenUserManager =
@@ -109,12 +111,14 @@
@Inject
public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager,
Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler,
- AccessibilityManager accessibilityManager) {
+ AccessibilityManager accessibilityManager,
+ HighPriorityProvider highPriorityProvider) {
mContext = context;
mVisualStabilityManager = visualStabilityManager;
mStatusBarLazy = statusBarLazy;
mMainHandler = mainHandler;
mAccessibilityManager = accessibilityManager;
+ mHighPriorityProvider = highPriorityProvider;
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -331,8 +335,7 @@
row.getIsNonblockable(),
isForBlockingHelper,
row.getEntry().getImportance(),
- row.getEntry().isHighPriority());
-
+ mHighPriorityProvider.isHighPriority(row.getEntry()));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index b4ccb56..edfd1b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -46,6 +46,7 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
@@ -269,7 +270,8 @@
}
mAppOpsItem = createAppOpsItem(mContext);
if (mIsUsingBidirectionalSwipe) {
- mInfoItem = createInfoItem(mContext, !mParent.getEntry().isHighPriority());
+ mInfoItem = createInfoItem(mContext,
+ mParent.getEntry().getBucket() == NotificationSectionsManager.BUCKET_SILENT);
} else {
mInfoItem = createInfoItem(mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index a3b1b5f..a6842ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -366,8 +366,8 @@
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, Handler.getMain(),
- UserHandle.ALL);
+ mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter,
+ Handler.getMain(), UserHandle.ALL);
notifyNavigationBarScreenOn();
mOverviewProxyService.addCallback(mOverviewProxyListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 5b34aa7..00e38f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -179,7 +179,7 @@
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- broadcastDispatcher.registerReceiver(mIntentReceiver, filter, mHandler);
+ broadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
// listen for user / profile change.
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index ddacc3a..4f0af9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -188,7 +188,7 @@
// NOTE: This receiver could run before this method returns, as it's not dispatching
// on the main thread and BroadcastDispatcher may not need to register with Context.
// The receiver will return immediately if the view does not have a Handler yet.
- mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter,
+ mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter,
Dependency.get(Dependency.TIME_TICK_HANDLER), UserHandle.ALL);
Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS,
StatusBarIconController.ICON_BLACKLIST);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index 2e26711..b4c154a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -96,7 +96,7 @@
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter,
+ mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter,
Dependency.get(Dependency.TIME_TICK_HANDLER));
updateClock();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 570f153..cb40d77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -79,7 +79,8 @@
IntentFilter filter = new IntentFilter();
filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
filter.addAction(LocationManager.MODE_CHANGED_ACTION);
- mBroadcastDispatcher.registerReceiver(this, filter, new Handler(bgLooper), UserHandle.ALL);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter,
+ new Handler(bgLooper), UserHandle.ALL);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mStatusBarManager
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 f640d03..679fa7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -326,7 +326,7 @@
filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- mBroadcastDispatcher.registerReceiver(this, filter, mReceiverHandler);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
updateMobileControllers();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 019ef3b..312c4ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -126,7 +126,8 @@
IntentFilter filter = new IntentFilter();
filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
- broadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, bgHandler, UserHandle.ALL);
+ broadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, bgHandler,
+ UserHandle.ALL);
// TODO: re-register network callback on user change.
mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 7758aba..31b9952 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -88,7 +88,7 @@
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() {
+ mBroadcastDispatcher.registerReceiverWithHandler(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index a4ed31d..112ae6f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -1008,7 +1008,7 @@
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- mBroadcastDispatcher.registerReceiver(this, filter, mWorker);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mWorker);
}
public void destroy() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 0c22aae..2e0fb3b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -150,10 +150,10 @@
@Test
public void testReceiversRegistered() {
- verify(mBroadcastDispatcher, atLeastOnce()).registerReceiver(
+ verify(mBroadcastDispatcher, atLeastOnce()).registerReceiverWithHandler(
eq(mKeyguardUpdateMonitor.mBroadcastReceiver),
any(IntentFilter.class), any(Handler.class));
- verify(mBroadcastDispatcher, atLeastOnce()).registerReceiver(
+ verify(mBroadcastDispatcher, atLeastOnce()).registerReceiverWithHandler(
eq(mKeyguardUpdateMonitor.mBroadcastAllReceiver),
any(IntentFilter.class), any(Handler.class), eq(UserHandle.ALL));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index 42fbf59..22b1837 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -27,6 +27,8 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
import junit.framework.Assert.assertSame
import org.junit.Before
import org.junit.Test
@@ -39,6 +41,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
@@ -73,6 +76,8 @@
@Mock
private lateinit var mockHandler: Handler
+ private lateinit var executor: Executor
+
@Captor
private lateinit var argumentCaptor: ArgumentCaptor<ReceiverData>
@@ -83,6 +88,7 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ executor = FakeExecutor(FakeSystemClock())
broadcastDispatcher = TestBroadcastDispatcher(
mockContext,
@@ -98,8 +104,9 @@
@Test
fun testAddingReceiverToCorrectUBR() {
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0)
- broadcastDispatcher.registerReceiver(
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, user0)
+ broadcastDispatcher.registerReceiverWithHandler(
broadcastReceiverOther, intentFilterOther, mockHandler, user1)
testableLooper.processAllMessages()
@@ -115,9 +122,29 @@
}
@Test
+ fun testAddingReceiverToCorrectUBR_executor() {
+ broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, executor, user0)
+ broadcastDispatcher.registerReceiver(
+ broadcastReceiverOther, intentFilterOther, executor, user1)
+
+ testableLooper.processAllMessages()
+
+ verify(mockUBRUser0).registerReceiver(capture(argumentCaptor))
+
+ assertSame(broadcastReceiver, argumentCaptor.value.receiver)
+ assertSame(intentFilter, argumentCaptor.value.filter)
+
+ verify(mockUBRUser1).registerReceiver(capture(argumentCaptor))
+ assertSame(broadcastReceiverOther, argumentCaptor.value.receiver)
+ assertSame(intentFilterOther, argumentCaptor.value.filter)
+ }
+
+ @Test
fun testRemovingReceiversRemovesFromAllUBR() {
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0)
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user1)
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, user0)
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, user1)
broadcastDispatcher.unregisterReceiver(broadcastReceiver)
@@ -129,8 +156,10 @@
@Test
fun testRemoveReceiverFromUser() {
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0)
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user1)
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, user0)
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, user1)
broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, user0)
@@ -143,8 +172,8 @@
@Test
fun testRegisterCurrentAsActualUser() {
setUserMock(mockContext, user1)
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler,
- UserHandle.CURRENT)
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, UserHandle.CURRENT)
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
index 21ed155..7821ae2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
@@ -26,6 +26,8 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
@@ -69,8 +71,6 @@
@Mock
private lateinit var mockContext: Context
@Mock
- private lateinit var mockHandler: Handler
- @Mock
private lateinit var mPendingResult: BroadcastReceiver.PendingResult
@Captor
@@ -81,12 +81,14 @@
private lateinit var intentFilter: IntentFilter
private lateinit var intentFilterOther: IntentFilter
private lateinit var handler: Handler
+ private lateinit var fakeExecutor: FakeExecutor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
handler = Handler(testableLooper.looper)
+ fakeExecutor = FakeExecutor(FakeSystemClock())
userBroadcastDispatcher = UserBroadcastDispatcher(
mockContext, USER_ID, handler, testableLooper.looper)
@@ -108,7 +110,7 @@
intentFilter = IntentFilter(ACTION_1)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
testableLooper.processAllMessages()
assertTrue(userBroadcastDispatcher.isRegistered())
@@ -128,7 +130,7 @@
intentFilter = IntentFilter(ACTION_1)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
testableLooper.processAllMessages()
reset(mockContext)
@@ -151,9 +153,9 @@
}
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiverOther, intentFilterOther, mockHandler, USER_HANDLE))
+ ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE))
testableLooper.processAllMessages()
assertTrue(userBroadcastDispatcher.isRegistered())
@@ -179,14 +181,15 @@
intentFilterOther = IntentFilter(ACTION_2)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE))
val intent = Intent(ACTION_2)
userBroadcastDispatcher.onReceive(mockContext, intent)
testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
verify(broadcastReceiver, never()).onReceive(any(), any())
verify(broadcastReceiverOther).onReceive(mockContext, intent)
@@ -198,14 +201,15 @@
intentFilterOther = IntentFilter(ACTION_2)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilterOther, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilterOther, fakeExecutor, USER_HANDLE))
val intent = Intent(ACTION_2)
userBroadcastDispatcher.onReceive(mockContext, intent)
testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
verify(broadcastReceiver).onReceive(mockContext, intent)
}
@@ -218,14 +222,15 @@
intentFilterOther.addCategory(CATEGORY_2)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE))
val intent = Intent(ACTION_1)
userBroadcastDispatcher.onReceive(mockContext, intent)
testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
verify(broadcastReceiver).onReceive(mockContext, intent)
verify(broadcastReceiverOther).onReceive(mockContext, intent)
@@ -235,12 +240,13 @@
fun testPendingResult() {
intentFilter = IntentFilter(ACTION_1)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
val intent = Intent(ACTION_1)
userBroadcastDispatcher.onReceive(mockContext, intent)
testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
verify(broadcastReceiver).onReceive(mockContext, intent)
verify(broadcastReceiver).pendingResult = mPendingResult
@@ -250,15 +256,16 @@
fun testRemoveReceiverReferences() {
intentFilter = IntentFilter(ACTION_1)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
intentFilterOther = IntentFilter(ACTION_1)
intentFilterOther.addAction(ACTION_2)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE))
userBroadcastDispatcher.unregisterReceiver(broadcastReceiver)
testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
assertFalse(userBroadcastDispatcher.isReceiverReferenceHeld(broadcastReceiver))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 167f361..548da8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -110,7 +110,7 @@
@Test
public void testReceiverIsRegisteredToDispatcherOnStart() {
mPowerUI.start();
- verify(mBroadcastDispatcher).registerReceiver(
+ verify(mBroadcastDispatcher).registerReceiverWithHandler(
any(BroadcastReceiver.class),
any(IntentFilter.class),
any(Handler.class)); //PowerUI does not call with User
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index c559265..cd33cf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -83,6 +83,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -233,7 +234,8 @@
mock(NotificationFilter.class),
mNotifLog,
mock(NotificationSectionsFeatureManager.class),
- mock(PeopleNotificationIdentifier.class)),
+ mock(PeopleNotificationIdentifier.class),
+ mock(HighPriorityProvider.class)),
mEnvironment,
mFeatureFlags
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java
deleted file mode 100644
index a06d6c1..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection;
-
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.RankingBuilder;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class GroupEntryTest extends SysuiTestCase {
- @Test
- public void testIsHighPriority_addChild() {
- // GIVEN a GroupEntry with a lowPrioritySummary and no children
- final GroupEntry parentEntry = new GroupEntry("test_group_key");
- final NotificationEntry lowPrioritySummary = createNotifEntry(false);
- setSummary(parentEntry, lowPrioritySummary);
- assertFalse(parentEntry.isHighPriority());
-
- // WHEN we add a high priority child and invalidate derived members
- addChild(parentEntry, createNotifEntry(true));
- parentEntry.invalidateDerivedMembers();
-
- // THEN the GroupEntry's priority is updated to high even though the summary is still low
- // priority
- assertTrue(parentEntry.isHighPriority());
- assertFalse(lowPrioritySummary.isHighPriority());
- }
-
- @Test
- public void testIsHighPriority_clearChildren() {
- // GIVEN a GroupEntry with a lowPrioritySummary and high priority children
- final GroupEntry parentEntry = new GroupEntry("test_group_key");
- setSummary(parentEntry, createNotifEntry(false));
- addChild(parentEntry, createNotifEntry(true));
- addChild(parentEntry, createNotifEntry(true));
- addChild(parentEntry, createNotifEntry(true));
- assertTrue(parentEntry.isHighPriority());
-
- // WHEN we clear the children and invalidate derived members
- parentEntry.clearChildren();
- parentEntry.invalidateDerivedMembers();
-
- // THEN the parentEntry isn't high priority anymore
- assertFalse(parentEntry.isHighPriority());
- }
-
- @Test
- public void testIsHighPriority_summaryUpdated() {
- // GIVEN a GroupEntry with a lowPrioritySummary and no children
- final GroupEntry parentEntry = new GroupEntry("test_group_key");
- final NotificationEntry lowPrioritySummary = createNotifEntry(false);
- setSummary(parentEntry, lowPrioritySummary);
- assertFalse(parentEntry.isHighPriority());
-
- // WHEN the summary changes to high priority and invalidates its derived members
- lowPrioritySummary.setRanking(
- new RankingBuilder()
- .setKey(lowPrioritySummary.getKey())
- .setImportance(IMPORTANCE_HIGH)
- .build());
- lowPrioritySummary.invalidateDerivedMembers();
- assertTrue(lowPrioritySummary.isHighPriority());
-
- // THEN the GroupEntry's priority is updated to high
- assertTrue(parentEntry.isHighPriority());
- }
-
- @Test
- public void testIsHighPriority_checkChildrenToCalculatePriority() {
- // GIVEN:
- // GroupEntry = parentEntry, summary = lowPrioritySummary
- // NotificationEntry = lowPriorityChild
- // NotificationEntry = highPriorityChild
- final GroupEntry parentEntry = new GroupEntry("test_group_key");
- setSummary(parentEntry, createNotifEntry(false));
- addChild(parentEntry, createNotifEntry(false));
- addChild(parentEntry, createNotifEntry(true));
-
- // THEN the GroupEntry parentEntry is high priority since it has a high priority child
- assertTrue(parentEntry.isHighPriority());
- }
-
- @Test
- public void testIsHighPriority_childEntryRankingUpdated() {
- // GIVEN:
- // GroupEntry = parentEntry, summary = lowPrioritySummary
- // NotificationEntry = lowPriorityChild
- final GroupEntry parentEntry = new GroupEntry("test_group_key");
- final NotificationEntry lowPriorityChild = createNotifEntry(false);
- setSummary(parentEntry, createNotifEntry(false));
- addChild(parentEntry, lowPriorityChild);
-
- // WHEN the child entry ranking changes to high priority and invalidates its derived members
- lowPriorityChild.setRanking(
- new RankingBuilder()
- .setKey(lowPriorityChild.getKey())
- .setImportance(IMPORTANCE_HIGH)
- .build());
- lowPriorityChild.invalidateDerivedMembers();
-
- // THEN the parent entry's high priority value is updated - but not the parent's summary
- assertTrue(parentEntry.isHighPriority());
- assertFalse(parentEntry.getSummary().isHighPriority());
- }
-
- private NotificationEntry createNotifEntry(boolean highPriority) {
- return new NotificationEntryBuilder()
- .setImportance(highPriority ? IMPORTANCE_HIGH : IMPORTANCE_MIN)
- .build();
- }
-
- private void setSummary(GroupEntry parent, NotificationEntry summary) {
- parent.setSummary(summary);
- summary.setParent(parent);
- }
-
- private void addChild(GroupEntry parent, NotificationEntry child) {
- parent.addChild(child);
- child.setParent(parent);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
new file mode 100644
index 0000000..93909dc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class HighPriorityProviderTest extends SysuiTestCase {
+ @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+ private HighPriorityProvider mHighPriorityProvider;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mHighPriorityProvider = new HighPriorityProvider(mPeopleNotificationIdentifier);
+ }
+
+ @Test
+ public void highImportance() {
+ // GIVEN notification has high importance
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+
+ // THEN it has high priority
+ assertTrue(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void peopleNotification() {
+ // GIVEN notification is low importance and is a people notification
+ final Notification notification = new Notification.Builder(mContext, "test")
+ .build();
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(IMPORTANCE_LOW)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
+
+ // THEN it has high priority
+ assertTrue(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void messagingStyle() {
+ // GIVEN notification is low importance but has messaging style
+ final Notification notification = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle(""))
+ .build();
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+
+ // THEN it has high priority
+ assertTrue(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void lowImportanceForeground() {
+ // GIVEN notification is low importance and is associated with a foreground service
+ final Notification notification = mock(Notification.class);
+ when(notification.isForegroundService()).thenReturn(true);
+
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(IMPORTANCE_LOW)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+
+ // THEN it has high priority
+ assertTrue(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void minImportanceForeground() {
+ // GIVEN notification is low importance and is associated with a foreground service
+ final Notification notification = mock(Notification.class);
+ when(notification.isForegroundService()).thenReturn(true);
+
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(IMPORTANCE_MIN)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+
+ // THEN it does NOT have high priority
+ assertFalse(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void userChangeTrumpsHighPriorityCharacteristics() {
+ // GIVEN notification has high priority characteristics but the user changed the importance
+ // to less than IMPORTANCE_DEFAULT (ie: IMPORTANCE_LOW or IMPORTANCE_MIN)
+ final Notification notification = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle(""))
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ final NotificationChannel channel = new NotificationChannel("a", "a",
+ IMPORTANCE_LOW);
+ channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .setChannel(channel)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
+
+ // THEN it does NOT have high priority
+ assertFalse(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void testIsHighPriority_summaryUpdated() {
+ // GIVEN a GroupEntry with a lowPrioritySummary and no children
+ final GroupEntry parentEntry = new GroupEntry("test_group_key");
+ final NotificationEntry lowPrioritySummary = createNotifEntry(false);
+ setSummary(parentEntry, lowPrioritySummary);
+ assertFalse(mHighPriorityProvider.isHighPriority(parentEntry));
+
+ // WHEN the summary changes to high priority
+ lowPrioritySummary.setRanking(
+ new RankingBuilder()
+ .setKey(lowPrioritySummary.getKey())
+ .setImportance(IMPORTANCE_HIGH)
+ .build());
+ assertTrue(mHighPriorityProvider.isHighPriority(lowPrioritySummary));
+
+ // THEN the GroupEntry's priority is updated to high
+ assertTrue(mHighPriorityProvider.isHighPriority(parentEntry));
+ }
+
+ @Test
+ public void testIsHighPriority_checkChildrenToCalculatePriority() {
+ // GIVEN:
+ // GroupEntry = parentEntry, summary = lowPrioritySummary
+ // NotificationEntry = lowPriorityChild
+ // NotificationEntry = highPriorityChild
+ final GroupEntry parentEntry = new GroupEntry("test_group_key");
+ setSummary(parentEntry, createNotifEntry(false));
+ addChild(parentEntry, createNotifEntry(false));
+ addChild(parentEntry, createNotifEntry(true));
+
+ // THEN the GroupEntry parentEntry is high priority since it has a high priority child
+ assertTrue(mHighPriorityProvider.isHighPriority(parentEntry));
+ }
+
+ @Test
+ public void testIsHighPriority_childEntryRankingUpdated() {
+ // GIVEN:
+ // GroupEntry = parentEntry, summary = lowPrioritySummary
+ // NotificationEntry = lowPriorityChild
+ final GroupEntry parentEntry = new GroupEntry("test_group_key");
+ final NotificationEntry lowPriorityChild = createNotifEntry(false);
+ setSummary(parentEntry, createNotifEntry(false));
+ addChild(parentEntry, lowPriorityChild);
+
+ // WHEN the child entry ranking changes to high priority
+ lowPriorityChild.setRanking(
+ new RankingBuilder()
+ .setKey(lowPriorityChild.getKey())
+ .setImportance(IMPORTANCE_HIGH)
+ .build());
+
+ // THEN the parent entry's high priority value is updated - but not the parent's summary
+ assertTrue(mHighPriorityProvider.isHighPriority(parentEntry));
+ assertFalse(mHighPriorityProvider.isHighPriority(parentEntry.getSummary()));
+ }
+
+ private NotificationEntry createNotifEntry(boolean highPriority) {
+ return new NotificationEntryBuilder()
+ .setImportance(highPriority ? IMPORTANCE_HIGH : IMPORTANCE_MIN)
+ .build();
+ }
+
+ private void setSummary(GroupEntry parent, NotificationEntry summary) {
+ parent.setSummary(summary);
+ summary.setParent(parent);
+ }
+
+ private void addChild(GroupEntry parent, NotificationEntry child) {
+ parent.addChild(child);
+ child.setParent(parent);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 39ae68a..5b0b668 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -21,8 +21,6 @@
import static android.app.Notification.CATEGORY_EVENT;
import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.Notification.CATEGORY_REMINDER;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
@@ -92,44 +90,6 @@
}
@Test
- public void testIsHighPriority_notificationUpdates() {
- // GIVEN a notification with high importance
- final NotificationEntry entryHigh = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- // WHEN we get the value for the high priority entry, we're caching the high priority value
- assertTrue(entryHigh.isHighPriority());
-
- // WHEN we change the ranking and derived members (high priority) are invalidated
- entryHigh.setRanking(
- new RankingBuilder()
- .setKey(entryHigh.getKey())
- .setImportance(IMPORTANCE_MIN)
- .build());
- entryHigh.invalidateDerivedMembers();
-
- // THEN the priority is recalculated and is now low
- assertFalse(entryHigh.isHighPriority());
-
- // WHEN the sbn is updated to have messaging style (high priority characteristic)
- // AND the entry invalidates its derived members
- final Notification notification =
- new Notification.Builder(mContext, "test")
- .setStyle(new Notification.MessagingStyle(""))
- .build();
- final StatusBarNotification sbn = entryHigh.getSbn();
- entryHigh.setSbn(new StatusBarNotification(
- sbn.getPackageName(), sbn.getPackageName(),
- sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
- notification, sbn.getUser(), sbn.getOverrideGroupKey(), 0));
- entryHigh.invalidateDerivedMembers();
-
- // THEN the priority is recalculated and is now high
- assertTrue(entryHigh.isHighPriority());
- }
-
- @Test
public void testIsExemptFromDndVisualSuppression_foreground() {
mEntry.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index 10450fa..e273191 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.logging.NotifLog
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
@@ -62,7 +63,8 @@
mock(NotificationFilter::class.java),
mock(NotifLog::class.java),
mock(NotificationSectionsFeatureManager::class.java),
- personNotificationIdentifier
+ personNotificationIdentifier,
+ HighPriorityProvider(personNotificationIdentifier)
)
}
@@ -182,7 +184,8 @@
filter: NotificationFilter,
notifLog: NotifLog,
sectionsFeatureManager: NotificationSectionsFeatureManager,
- peopleNotificationIdentifier: PeopleNotificationIdentifier
+ peopleNotificationIdentifier: PeopleNotificationIdentifier,
+ highPriorityProvider: HighPriorityProvider
) : NotificationRankingManager(
mediaManager,
groupManager,
@@ -190,7 +193,8 @@
filter,
notifLog,
sectionsFeatureManager,
- peopleNotificationIdentifier
+ peopleNotificationIdentifier,
+ highPriorityProvider
) {
fun applyTestRankingMap(r: RankingMap) {
rankingMap = r
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index 979b8a9..f921cf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
@@ -66,6 +67,7 @@
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private NotifListBuilderImpl mNotifListBuilder;
private NotificationEntry mEntry;
@@ -78,7 +80,7 @@
mKeyguardCoordinator = new KeyguardCoordinator(
mContext, mMainHandler, mKeyguardStateController, mLockscreenUserManager,
mBroadcastDispatcher, mStatusBarStateController,
- mKeyguardUpdateMonitor);
+ mKeyguardUpdateMonitor, mHighPriorityProvider);
mEntry = new NotificationEntryBuilder()
.setUser(new UserHandle(NOTIF_USER_ID))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java
deleted file mode 100644
index 6fa1a89..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.provider;
-
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.Person;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class IsHighPriorityProviderTest extends SysuiTestCase {
- private IsHighPriorityProvider mIsHighPriorityProvider;
-
- @Before
- public void setup() {
- mIsHighPriorityProvider = new IsHighPriorityProvider();
- }
-
- @Test
- public void testCache() {
- // GIVEN a notification with high importance
- final NotificationEntry entryHigh = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- // GIVEN notification with min importance
- final NotificationEntry entryMin = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_MIN)
- .build();
-
- // WHEN we get the value for the high priority entry
- assertTrue(mIsHighPriorityProvider.get(entryHigh));
-
- // THEN the value is cached, so even when passed an entryMin, we still get high priority
- assertTrue(mIsHighPriorityProvider.get(entryMin));
-
- // UNTIL the provider is invalidated
- mIsHighPriorityProvider.invalidate();
-
- // THEN the priority is recalculated
- assertFalse(mIsHighPriorityProvider.get(entryMin));
- }
-
- @Test
- public void highImportance() {
- // GIVEN notification has high importance
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- // THEN it has high priority
- assertTrue(mIsHighPriorityProvider.get(entry));
- }
-
- @Test
- public void peopleNotification() {
- // GIVEN notification is low importance but has a person associated with it
- final Notification notification = new Notification.Builder(mContext, "test")
- .addPerson(
- new Person.Builder()
- .setName("name")
- .setKey("abc")
- .setUri("uri")
- .setBot(true)
- .build())
- .build();
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setImportance(IMPORTANCE_LOW)
- .build();
-
- // THEN it has high priority
- assertTrue(mIsHighPriorityProvider.get(entry));
- }
-
- @Test
- public void messagingStyle() {
- // GIVEN notification is low importance but has messaging style
- final Notification notification = new Notification.Builder(mContext, "test")
- .setStyle(new Notification.MessagingStyle(""))
- .build();
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .build();
-
- // THEN it has high priority
- assertTrue(mIsHighPriorityProvider.get(entry));
- }
-
- @Test
- public void lowImportanceForeground() {
- // GIVEN notification is low importance and is associated with a foreground service
- final Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setImportance(IMPORTANCE_LOW)
- .build();
-
- // THEN it has high priority
- assertTrue(mIsHighPriorityProvider.get(entry));
- }
-
- @Test
- public void minImportanceForeground() {
- // GIVEN notification is low importance and is associated with a foreground service
- final Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setImportance(IMPORTANCE_MIN)
- .build();
-
- // THEN it does NOT have high priority
- assertFalse(mIsHighPriorityProvider.get(entry));
- }
-
- @Test
- public void userChangeTrumpsHighPriorityCharacteristics() {
- // GIVEN notification has high priority characteristics but the user changed the importance
- // to less than IMPORTANCE_DEFAULT (ie: IMPORTANCE_LOW or IMPORTANCE_MIN)
- final Notification notification = new Notification.Builder(mContext, "test")
- .addPerson(
- new Person.Builder()
- .setName("name")
- .setKey("abc")
- .setUri("uri")
- .setBot(true)
- .build())
- .setStyle(new Notification.MessagingStyle(""))
- .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
- .build();
-
- final NotificationChannel channel = new NotificationChannel("a", "a",
- IMPORTANCE_LOW);
- channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setChannel(channel)
- .build();
-
- // THEN it does NOT have high priority
- assertFalse(mIsHighPriorityProvider.get(entry));
- }
-}
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 ccc9496..4e27770 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
@@ -70,6 +70,7 @@
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -113,6 +114,7 @@
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private StatusBar mStatusBar;
@Mock private AccessibilityManager mAccessibilityManager;
+ @Mock private HighPriorityProvider mHighPriorityProvider;
@Before
public void setUp() {
@@ -128,7 +130,7 @@
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager,
- () -> mStatusBar, mHandler, mAccessibilityManager);
+ () -> mStatusBar, mHandler, mAccessibilityManager, mHighPriorityProvider);
mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
mCheckSaveListener, mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -391,6 +393,7 @@
.build();
when(row.getIsNonblockable()).thenReturn(false);
+ when(mHighPriorityProvider.isHighPriority(entry)).thenReturn(true);
StatusBarNotification statusBarNotification = entry.getSbn();
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 003d803..518b670 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -267,8 +267,6 @@
ExpandableNotificationRow notifRow = mock(ExpandableNotificationRow.class,
RETURNS_DEEP_STUBS);
when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
- when(notifRow.getEntry().isHighPriority())
- .thenReturn(children[i] == ChildType.HIPRI);
when(notifRow.getEntry().getBucket()).thenReturn(
children[i] == ChildType.HIPRI ? BUCKET_ALERTING : BUCKET_SILENT);
when(notifRow.getParent()).thenReturn(mNssl);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 39f037c..ea8d4ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -165,7 +166,8 @@
mock(NotificationFilter.class),
mock(NotifLog.class),
mock(NotificationSectionsFeatureManager.class),
- mock(PeopleNotificationIdentifier.class)
+ mock(PeopleNotificationIdentifier.class),
+ mock(HighPriorityProvider.class)
),
mock(NotificationEntryManager.KeyguardEnvironment.class),
mock(FeatureFlags.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 39afbe0..8f645b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -190,7 +190,7 @@
mFragments.dispatchResume();
processAllMessages();
- verify(mBroadcastDispatcher).registerReceiver(
+ verify(mBroadcastDispatcher).registerReceiverWithHandler(
any(BroadcastReceiver.class),
any(IntentFilter.class),
any(Handler.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 2854665..80aa6f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -69,7 +69,7 @@
@Test
public void testRegisteredWithDispatcher() {
- verify(mBroadcastDispatcher).registerReceiver(any(BroadcastReceiver.class),
+ verify(mBroadcastDispatcher).registerReceiverWithHandler(any(BroadcastReceiver.class),
any(IntentFilter.class),
any(Handler.class)); // VolumeDialogControllerImpl does not call with user
}
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 797b713..d297f3f 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -123,4 +123,5 @@
use_embedded_native_libs: true,
// The permission configuration *must* be included to ensure security of the device
required: ["NetworkPermissionConfig"],
+ apex_available: ["com.android.tethering"],
}
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 5785707..264ce44 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -47,6 +47,16 @@
libs: [
"android_system_stubs_current",
],
+
+ hostdex: true, // for hiddenapi check
+ visibility: [
+ "//frameworks/base/packages/Tethering:__subpackages__",
+ //TODO(b/147200698) remove below lines when the platform is built with stubs
+ "//frameworks/base",
+ "//frameworks/base/services",
+ "//frameworks/base/services/core",
+ ],
+ apex_available: ["com.android.tethering"],
}
filegroup {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 3bce322..d45a54e 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1452,6 +1452,11 @@
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
return;
}
+ dumpWithoutCheckingPermission(fd, pw, args);
+ }
+
+ @VisibleForTesting
+ void dumpWithoutCheckingPermission(FileDescriptor fd, PrintWriter pw, String[] args) {
int userId = binderGetCallingUserId();
if (!isUserReadyForBackup(userId)) {
pw.println("Inactive");
@@ -1460,7 +1465,16 @@
if (args != null) {
for (String arg : args) {
- if ("users".equals(arg.toLowerCase())) {
+ if ("-h".equals(arg)) {
+ pw.println("'dumpsys backup' optional arguments:");
+ pw.println(" -h : this help text");
+ pw.println(" a[gents] : dump information about defined backup agents");
+ pw.println(" transportclients : dump information about transport clients");
+ pw.println(" transportstats : dump transport statts");
+ pw.println(" users : dump the list of users for which backup service "
+ + "is running");
+ return;
+ } else if ("users".equals(arg.toLowerCase())) {
pw.print(DUMP_RUNNING_USERS_MESSAGE);
for (int i = 0; i < mUserServices.size(); i++) {
pw.print(" " + mUserServices.keyAt(i));
@@ -1471,11 +1485,12 @@
}
}
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.dump(fd, pw, args);
+ for (int i = 0; i < mUserServices.size(); i++) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(mUserServices.keyAt(i), "dump()");
+ if (userBackupManagerService != null) {
+ userBackupManagerService.dump(fd, pw, args);
+ }
}
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 064cd06..7b95ab5 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -3545,14 +3545,7 @@
try {
if (args != null) {
for (String arg : args) {
- if ("-h".equals(arg)) {
- pw.println("'dumpsys backup' optional arguments:");
- pw.println(" -h : this help text");
- pw.println(" a[gents] : dump information about defined backup agents");
- pw.println(" users : dump the list of users for which backup service "
- + "is running");
- return;
- } else if ("agents".startsWith(arg)) {
+ if ("agents".startsWith(arg)) {
dumpAgents(pw);
return;
} else if ("transportclients".equals(arg.toLowerCase())) {
@@ -3583,8 +3576,10 @@
}
private void dumpInternal(PrintWriter pw) {
+ // Add prefix for only non-system users so that system user dumpsys is the same as before
+ String userPrefix = mUserId == UserHandle.USER_SYSTEM ? "" : "User " + mUserId + ":";
synchronized (mQueueLock) {
- pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+ pw.println(userPrefix + "Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+ " / " + (!mSetupComplete ? "not " : "") + "setup complete / "
+ (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
@@ -3594,13 +3589,13 @@
+ " (now = " + System.currentTimeMillis() + ')');
pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId));
- pw.println("Transport whitelist:");
+ pw.println(userPrefix + "Transport whitelist:");
for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
pw.print(" ");
pw.println(transport.flattenToShortString());
}
- pw.println("Available transports:");
+ pw.println(userPrefix + "Available transports:");
final String[] transports = listAllTransports();
if (transports != null) {
for (String t : transports) {
@@ -3626,18 +3621,18 @@
mTransportManager.dumpTransportClients(pw);
- pw.println("Pending init: " + mPendingInits.size());
+ pw.println(userPrefix + "Pending init: " + mPendingInits.size());
for (String s : mPendingInits) {
pw.println(" " + s);
}
- pw.print("Ancestral: ");
+ pw.print(userPrefix + "Ancestral: ");
pw.println(Long.toHexString(mAncestralToken));
- pw.print("Current: ");
+ pw.print(userPrefix + "Current: ");
pw.println(Long.toHexString(mCurrentToken));
int numPackages = mBackupParticipants.size();
- pw.println("Participants:");
+ pw.println(userPrefix + "Participants:");
for (int i = 0; i < numPackages; i++) {
int uid = mBackupParticipants.keyAt(i);
pw.print(" uid: ");
@@ -3648,7 +3643,7 @@
}
}
- pw.println("Ancestral packages: "
+ pw.println(userPrefix + "Ancestral packages: "
+ (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
if (mAncestralPackages != null) {
for (String pkg : mAncestralPackages) {
@@ -3657,17 +3652,17 @@
}
Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
- pw.println("Ever backed up: " + processedPackages.size());
+ pw.println(userPrefix + "Ever backed up: " + processedPackages.size());
for (String pkg : processedPackages) {
pw.println(" " + pkg);
}
- pw.println("Pending key/value backup: " + mPendingBackups.size());
+ pw.println(userPrefix + "Pending key/value backup: " + mPendingBackups.size());
for (BackupRequest req : mPendingBackups.values()) {
pw.println(" " + req);
}
- pw.println("Full backup queue:" + mFullBackupQueue.size());
+ pw.println(userPrefix + "Full backup queue:" + mFullBackupQueue.size());
for (FullBackupEntry entry : mFullBackupQueue) {
pw.print(" ");
pw.print(entry.lastBackup);
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index 70569db..5179fa7 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -38,11 +38,13 @@
import android.view.IGraphicsStatsCallback;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FastPrintWriter;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -78,6 +80,8 @@
private static final int SAVE_BUFFER = 1;
private static final int DELETE_OLD = 2;
+ private static final int AID_STATSD = 1066; // Statsd uid is set to 1066 forever.
+
// This isn't static because we need this to happen after registerNativeMethods, however
// the class is loaded (and thus static ctor happens) before that occurs.
private final int ASHMEM_SIZE = nGetAshmemSize();
@@ -121,6 +125,7 @@
return true;
}
});
+ nativeInit();
}
/**
@@ -186,6 +191,86 @@
return pfd;
}
+ // If lastFullDay is true, pullGraphicsStats returns stats for the last complete day/24h period
+ // that does not include today. If lastFullDay is false, pullGraphicsStats returns stats for the
+ // current day.
+ // This method is invoked from native code only.
+ @SuppressWarnings({"UnusedDeclaration"})
+ private long pullGraphicsStats(boolean lastFullDay) throws RemoteException {
+ int uid = Binder.getCallingUid();
+
+ // DUMP and PACKAGE_USAGE_STATS permissions are required to invoke this method.
+ // TODO: remove exception for statsd daemon after required permissions are granted. statsd
+ // TODO: should have these permissions granted by data/etc/platform.xml, but it does not.
+ if (uid != AID_STATSD) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new FastPrintWriter(sw);
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
+ pw.flush();
+ throw new RemoteException(sw.toString());
+ }
+ }
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return pullGraphicsStatsImpl(lastFullDay);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ private long pullGraphicsStatsImpl(boolean lastFullDay) {
+ long targetDay;
+ if (lastFullDay) {
+ // Get stats from yesterday. Stats stay constant, because the day is over.
+ targetDay = normalizeDate(System.currentTimeMillis() - 86400000).getTimeInMillis();
+ } else {
+ // Get stats from today. Stats may change as more apps are run today.
+ targetDay = normalizeDate(System.currentTimeMillis()).getTimeInMillis();
+ }
+
+ // Find active buffers for targetDay.
+ ArrayList<HistoricalBuffer> buffers;
+ synchronized (mLock) {
+ buffers = new ArrayList<>(mActive.size());
+ for (int i = 0; i < mActive.size(); i++) {
+ ActiveBuffer buffer = mActive.get(i);
+ if (buffer.mInfo.startTime == targetDay) {
+ try {
+ buffers.add(new HistoricalBuffer(buffer));
+ } catch (IOException ex) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ // Dump active and historic buffers for targetDay in a serialized
+ // GraphicsStatsServiceDumpProto proto.
+ long dump = nCreateDump(-1, true);
+ try {
+ synchronized (mFileAccessLock) {
+ HashSet<File> skipList = dumpActiveLocked(dump, buffers);
+ buffers.clear();
+ String subPath = String.format("%d", targetDay);
+ File dateDir = new File(mGraphicsStatsDir, subPath);
+ if (dateDir.exists()) {
+ for (File pkg : dateDir.listFiles()) {
+ for (File version : pkg.listFiles()) {
+ File data = new File(version, "total");
+ if (skipList.contains(data)) {
+ continue;
+ }
+ nAddToDump(dump, data.getAbsolutePath());
+ }
+ }
+ }
+ }
+ } finally {
+ return nFinishDumpInMemory(dump);
+ }
+ }
+
private ParcelFileDescriptor getPfd(MemoryFile file) {
try {
if (!file.getFileDescriptor().valid()) {
@@ -379,12 +464,21 @@
}
}
+ @Override
+ protected void finalize() throws Throwable {
+ nativeDestructor();
+ }
+
+ private native void nativeInit();
+ private static native void nativeDestructor();
+
private static native int nGetAshmemSize();
private static native long nCreateDump(int outFd, boolean isProto);
private static native void nAddToDump(long dump, String path, String packageName,
long versionCode, long startTime, long endTime, byte[] data);
private static native void nAddToDump(long dump, String path);
private static native void nFinishDump(long dump);
+ private static native long nFinishDumpInMemory(long dump);
private static native void nSaveBuffer(String path, String packageName, long versionCode,
long startTime, long endTime, byte[] data);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 3e6ccb5..0be21c5 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2194,6 +2194,17 @@
private static final String PHONE_CONSTANTS_STATE_KEY = "state";
private static final String PHONE_CONSTANTS_SUBSCRIPTION_KEY = "subscription";
+ /**
+ * Broadcast Action: The phone's signal strength has changed. The intent will have the
+ * following extra values:
+ * phoneName - A string version of the phone name.
+ * asu - A numeric value for the signal strength.
+ * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu).
+ * The following special values are defined:
+ * 0 means "-113 dBm or less".31 means "-51 dBm or greater".
+ */
+ public static final String ACTION_SIGNAL_STRENGTH_CHANGED = "android.intent.action.SIG_STR";
+
private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
long ident = Binder.clearCallingIdentity();
try {
@@ -2228,7 +2239,7 @@
Binder.restoreCallingIdentity(ident);
}
- Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
+ Intent intent = new Intent(ACTION_SIGNAL_STRENGTH_CHANGED);
Bundle data = new Bundle();
fillInSignalStrengthNotifierBundle(signalStrength, data);
intent.putExtras(data);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a58bd9b..e2a036a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2068,7 +2068,7 @@
+ " type=" + resolvedType + " callingUid=" + callingUid);
userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
- ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service",
+ ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE, "service",
callingPackage);
ServiceMap smap = getServiceMapLocked(userId);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index eb1ab38..2b20782 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -16,12 +16,14 @@
package com.android.server.am;
+import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
import static android.app.ActivityManager.USER_OP_SUCCESS;
+import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
@@ -1641,9 +1643,10 @@
if (callingUid != 0 && callingUid != SYSTEM_UID) {
final boolean allow;
+ final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, targetUserId);
if (mInjector.isCallerRecents(callingUid)
&& callingUserId == getCurrentUserId()
- && isSameProfileGroup(callingUserId, targetUserId)) {
+ && isSameProfileGroup) {
// If the caller is Recents and it is running in the current user, we then allow it
// to access its profiles.
allow = true;
@@ -1654,6 +1657,9 @@
} else if (allowMode == ALLOW_FULL_ONLY) {
// We require full access, sucks to be you.
allow = false;
+ } else if (canInteractWithAcrossProfilesPermission(
+ allowMode, isSameProfileGroup, callingPid, callingUid)) {
+ allow = true;
} else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
// If the caller does not have either permission, they are always doomed.
@@ -1661,10 +1667,11 @@
} else if (allowMode == ALLOW_NON_FULL) {
// We are blanket allowing non-full access, you lucky caller!
allow = true;
- } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) {
+ } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE
+ || allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
// We may or may not allow this depending on whether the two users are
// in the same profile.
- allow = isSameProfileGroup(callingUserId, targetUserId);
+ allow = isSameProfileGroup;
} else {
throw new IllegalArgumentException("Unknown mode: " + allowMode);
}
@@ -1690,6 +1697,11 @@
if (allowMode != ALLOW_FULL_ONLY) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_USERS);
+ if (isSameProfileGroup
+ && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+ builder.append(" or ");
+ builder.append(INTERACT_ACROSS_PROFILES);
+ }
}
String msg = builder.toString();
Slog.w(TAG, msg);
@@ -1710,6 +1722,19 @@
return targetUserId;
}
+ private boolean canInteractWithAcrossProfilesPermission(
+ int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid) {
+ if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+ return false;
+ }
+ if (!isSameProfileGroup) {
+ return false;
+ }
+ return mInjector.checkComponentPermission(
+ INTERACT_ACROSS_PROFILES, callingPid, callingUid, /*owningUid= */-1,
+ /*exported= */true) == PackageManager.PERMISSION_GRANTED;
+ }
+
int unsafeConvertIncomingUser(@UserIdInt int userId) {
return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF)
? getCurrentUserId(): userId;
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index d66fd57..da2ca9b 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -43,7 +43,8 @@
}
record.updateNotificationChannel(mConfig.getNotificationChannel(record.sbn.getPackageName(),
- record.sbn.getUid(), record.getChannel().getId(), false));
+ record.sbn.getUid(), record.getChannel().getId(),
+ record.getNotification().getShortcutId(), false));
return null;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 728b297..5eafb41 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -25,6 +25,7 @@
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
+import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
@@ -236,6 +237,7 @@
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.TriPredicate;
import com.android.server.DeviceIdleInternal;
@@ -2219,8 +2221,8 @@
maybeNotifyChannelOwner(pkg, uid, preUpdate, channel);
if (!fromListener) {
- final NotificationChannel modifiedChannel =
- mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
+ final NotificationChannel modifiedChannel = mPreferencesHelper.getNotificationChannel(
+ pkg, uid, channel.getId(), false);
mListeners.notifyNotificationChannelChanged(
pkg, UserHandle.getUserHandleForUid(uid),
modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
@@ -3017,21 +3019,43 @@
@Override
public void createNotificationChannels(String pkg,
- ParceledListSlice channelsList) throws RemoteException {
+ ParceledListSlice channelsList) {
checkCallerIsSystemOrSameApp(pkg);
createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
}
@Override
public void createNotificationChannelsForPackage(String pkg, int uid,
- ParceledListSlice channelsList) throws RemoteException {
- checkCallerIsSystem();
+ ParceledListSlice channelsList) {
+ enforceSystemOrSystemUI("only system can call this");
createNotificationChannelsImpl(pkg, uid, channelsList);
}
@Override
+ public void createConversationNotificationChannelForPackage(String pkg, int uid,
+ NotificationChannel parentChannel, String conversationId) {
+ enforceSystemOrSystemUI("only system can call this");
+ Preconditions.checkNotNull(parentChannel);
+ Preconditions.checkNotNull(conversationId);
+ String parentId = parentChannel.getId();
+ NotificationChannel conversationChannel = parentChannel;
+ conversationChannel.setId(String.format(
+ CONVERSATION_CHANNEL_ID_FORMAT, parentId, conversationId));
+ conversationChannel.setConversationId(parentId, conversationId);
+ createNotificationChannelsImpl(
+ pkg, uid, new ParceledListSlice(Arrays.asList(conversationChannel)));
+ }
+
+ @Override
public NotificationChannel getNotificationChannel(String callingPkg, int userId,
String targetPkg, String channelId) {
+ return getConversationNotificationChannel(
+ callingPkg, userId, targetPkg, channelId, null);
+ }
+
+ @Override
+ public NotificationChannel getConversationNotificationChannel(String callingPkg, int userId,
+ String targetPkg, String channelId, String conversationId) {
if (canNotifyAsPackage(callingPkg, targetPkg, userId)
|| isCallingUidSystem()) {
int targetUid = -1;
@@ -3041,7 +3065,8 @@
/* ignore */
}
return mPreferencesHelper.getNotificationChannel(
- targetPkg, targetUid, channelId, false /* includeDeleted */);
+ targetPkg, targetUid, channelId, conversationId,
+ false /* includeDeleted */);
}
throw new SecurityException("Pkg " + callingPkg
+ " cannot read channels for " + targetPkg + " in " + userId);
@@ -3072,6 +3097,30 @@
}
@Override
+ public void deleteConversationNotificationChannels(String pkg, int uid,
+ String conversationId) {
+ checkCallerIsSystem();
+ final int callingUid = Binder.getCallingUid();
+ List<NotificationChannel> channels =
+ mPreferencesHelper.getNotificationChannelsByConversationId(
+ pkg, uid, conversationId);
+ if (!channels.isEmpty()) {
+ for (NotificationChannel nc : channels) {
+ cancelAllNotificationsInt(MY_UID, MY_PID, pkg, nc.getId(), 0, 0, true,
+ UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
+ mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, nc.getId());
+ mListeners.notifyNotificationChannelChanged(pkg,
+ UserHandle.getUserHandleForUid(callingUid),
+ mPreferencesHelper.getNotificationChannel(
+ pkg, callingUid, nc.getId(), true),
+ NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+ }
+ handleSavePolicyFile();
+ }
+ }
+
+
+ @Override
public NotificationChannelGroup getNotificationChannelGroup(String pkg, String groupId) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getNotificationChannelGroupWithChannels(
@@ -5203,7 +5252,8 @@
channelId = (new Notification.TvExtender(notification)).getChannelId();
}
final NotificationChannel channel = mPreferencesHelper.getNotificationChannel(pkg,
- notificationUid, channelId, false /* includeDeleted */);
+ notificationUid, channelId, notification.getShortcutId(),
+ false /* includeDeleted */);
if (channel == null) {
final String noChannelStr = "No Channel found for "
+ "pkg=" + pkg
diff --git a/services/core/java/com/android/server/notification/OWNERS b/services/core/java/com/android/server/notification/OWNERS
new file mode 100644
index 0000000..5a19656
--- /dev/null
+++ b/services/core/java/com/android/server/notification/OWNERS
@@ -0,0 +1,4 @@
+dsandler@android.com
+juliacr@google.com
+beverlyt@google.com
+pixel@google.com
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index cdb0a17..92fcb7f 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -770,6 +770,13 @@
channel.setShowBadge(false);
}
channel.setOriginalImportance(channel.getImportance());
+
+ // validate parent
+ if (channel.getParentChannelId() != null) {
+ Preconditions.checkArgument(r.channels.containsKey(channel.getParentChannelId()),
+ "Tried to create a conversation channel without a preexisting parent");
+ }
+
r.channels.put(channel.getId(), channel);
if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
updateChannelsBypassingDnd(mContext.getUserId());
@@ -851,6 +858,13 @@
public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
boolean includeDeleted) {
Objects.requireNonNull(pkg);
+ return getNotificationChannel(pkg, uid, channelId, null, includeDeleted);
+ }
+
+ @Override
+ public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
+ String conversationId, boolean includeDeleted) {
+ Preconditions.checkNotNull(pkg);
synchronized (mPackagePreferences) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
if (r == null) {
@@ -859,14 +873,51 @@
if (channelId == null) {
channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
}
- final NotificationChannel nc = r.channels.get(channelId);
- if (nc != null && (includeDeleted || !nc.isDeleted())) {
- return nc;
+ if (conversationId == null) {
+ final NotificationChannel nc = r.channels.get(channelId);
+ if (nc != null && (includeDeleted || !nc.isDeleted())) {
+ return nc;
+ }
+ } else {
+ // look for an automatically created conversation specific channel
+ return findConversationChannel(r, channelId, conversationId, includeDeleted);
}
return null;
}
}
+ private NotificationChannel findConversationChannel(PackagePreferences p, String parentId,
+ String conversationId, boolean includeDeleted) {
+ for (NotificationChannel nc : p.channels.values()) {
+ if (conversationId.equals(nc.getConversationId())
+ && parentId.equals(nc.getParentChannelId())
+ && (includeDeleted || !nc.isDeleted())) {
+ return nc;
+ }
+ }
+ return null;
+ }
+
+ public List<NotificationChannel> getNotificationChannelsByConversationId(String pkg, int uid,
+ String conversationId) {
+ Preconditions.checkNotNull(pkg);
+ Preconditions.checkNotNull(conversationId);
+ List<NotificationChannel> channels = new ArrayList<>();
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return channels;
+ }
+ for (NotificationChannel nc : r.channels.values()) {
+ if (conversationId.equals(nc.getConversationId())
+ && !nc.isDeleted()) {
+ channels.add(nc);
+ }
+ }
+ return channels;
+ }
+ }
+
@Override
public void deleteNotificationChannel(String pkg, int uid, String channelId) {
synchronized (mPackagePreferences) {
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 7816f36..4b044c1 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -41,10 +41,15 @@
int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty);
boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp, boolean hasDndAccess);
- void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser);
- NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
+ void updateNotificationChannel(String pkg, int uid, NotificationChannel channel,
+ boolean fromUser);
+ NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
+ boolean includeDeleted);
+ NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
+ String conversationId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannels(String pkg, int uid);
- ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, boolean includeDeleted);
+ ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
+ boolean includeDeleted);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a9571d9..24180f2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5344,8 +5344,9 @@
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /* requireFullPermission */, false /* checkShell */, "get service info");
+ mPermissionManager.enforceCrossUserOrProfilePermission(
+ callingUid, userId, false /* requireFullPermission */, false /* checkShell */,
+ "get service info");
synchronized (mLock) {
ParsedService s = mComponentResolver.getService(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -7795,8 +7796,10 @@
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /*requireFullPermission*/, false /*checkShell*/,
+ mPermissionManager.enforceCrossUserOrProfilePermission(callingUid,
+ userId,
+ false /*requireFullPermission*/,
+ false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps);
@@ -19380,7 +19383,7 @@
// PermissionController manages default home directly.
return false;
}
- mPermissionManager.setDefaultHome(currentPackageName, userId, (successful) -> {
+ mPermissionManager.setDefaultHome(packageName, userId, (successful) -> {
if (successful) {
postPreferredActivityChangedBroadcast(userId);
}
@@ -20122,8 +20125,7 @@
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
// disabled after already being started.
CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
- mPermissionManagerService, mContext.getContentResolver(),
- UserHandle.USER_SYSTEM);
+ mPermissionManagerService, UserHandle.USER_SYSTEM, mContext);
disableSkuSpecificApps();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d921f31..d8c1966 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -4005,21 +4005,128 @@
PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
}
- if (!requirePermissionWhenSameUser && userId == UserHandle.getUserId(callingUid)) return;
- if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) {
- if (requireFullPermission) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- } else {
- try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- } catch (SecurityException se) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS, message);
- }
- }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (hasCrossUserPermission(
+ callingUid, callingUserId, userId, requireFullPermission,
+ requirePermissionWhenSameUser)) {
+ return;
}
+ String errorMessage = buildInvalidCrossUserPermissionMessage(
+ message, requireFullPermission);
+ Slog.w(TAG, errorMessage);
+ throw new SecurityException(errorMessage);
+ }
+
+ /**
+ * Checks if the request is from the system or an app that has the appropriate cross-user
+ * permissions defined as follows:
+ * <ul>
+ * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li>
+ * <li>INTERACT_ACROSS_USERS if the given {@userId} is in a different profile group
+ * to the caller.</li>
+ * <li>Otherwise, INTERACT_ACROSS_PROFILES if the given {@userId} is in the same profile group
+ * as the caller.</li>
+ * </ul>
+ *
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
+ private void enforceCrossUserOrProfilePermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell,
+ String message) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userId);
+ }
+ if (checkShell) {
+ PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
+ /*requirePermissionWhenSameUser= */ false)) {
+ return;
+ }
+ final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
+ if (isSameProfileGroup
+ && hasPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)) {
+ return;
+ }
+ String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
+ message, requireFullPermission, isSameProfileGroup);
+ Slog.w(TAG, errorMessage);
+ throw new SecurityException(errorMessage);
+ }
+
+ private boolean hasCrossUserPermission(
+ int callingUid, int callingUserId, int userId, boolean requireFullPermission,
+ boolean requirePermissionWhenSameUser) {
+ if (!requirePermissionWhenSameUser && userId == callingUserId) {
+ return true;
+ }
+ if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) {
+ return true;
+ }
+ if (requireFullPermission) {
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ }
+ return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ }
+
+ private boolean hasPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private static String buildInvalidCrossUserPermissionMessage(
+ String message, boolean requireFullPermission) {
+ StringBuilder builder = new StringBuilder();
+ if (message != null) {
+ builder.append(message);
+ builder.append(": ");
+ }
+ builder.append("Requires ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ if (requireFullPermission) {
+ builder.append(".");
+ return builder.toString();
+ }
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ builder.append(".");
+ return builder.toString();
+ }
+
+ private static String buildInvalidCrossUserOrProfilePermissionMessage(
+ String message, boolean requireFullPermission, boolean isSameProfileGroup) {
+ StringBuilder builder = new StringBuilder();
+ if (message != null) {
+ builder.append(message);
+ builder.append(": ");
+ }
+ builder.append("Requires ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ if (requireFullPermission) {
+ builder.append(".");
+ return builder.toString();
+ }
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ if (isSameProfileGroup) {
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES);
+ }
+ builder.append(".");
+ return builder.toString();
}
@GuardedBy({"mSettings.mLock", "mLock"})
@@ -4215,6 +4322,17 @@
PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
requireFullPermission, checkShell, requirePermissionWhenSameUser, message);
}
+
+ @Override
+ public void enforceCrossUserOrProfilePermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell, String message) {
+ PermissionManagerService.this.enforceCrossUserOrProfilePermission(callingUid,
+ userId,
+ requireFullPermission,
+ checkShell,
+ message);
+ }
+
@Override
public void enforceGrantRevokeRuntimePermissionPermissions(String message) {
PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 0f22619..58a9f42 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -271,6 +271,15 @@
*/
public abstract void enforceCrossUserPermission(int callingUid, int userId,
boolean requireFullPermission, boolean checkShell, @NonNull String message);
+
+ /**
+ * Similar to {@link #enforceCrossUserPermission(int, int, boolean, boolean, String)}
+ * but also allows INTERACT_ACROSS_PROFILES permission if calling user and {@code userId} are
+ * in the same profile group.
+ */
+ public abstract void enforceCrossUserOrProfilePermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell, @NonNull String message);
+
/**
* @see #enforceCrossUserPermission(int, int, boolean, boolean, String)
* @param requirePermissionWhenSameUser When {@code true}, still require the cross user
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 0b89646..8385f40 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -21,6 +21,7 @@
import android.hardware.audio.common.V2_0.Uuid;
import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
+import android.hardware.soundtrigger.V2_3.Properties;
import android.media.audio.common.AudioConfig;
import android.media.audio.common.AudioOffloadInfo;
import android.media.soundtrigger_middleware.ConfidenceLevel;
@@ -69,6 +70,13 @@
return aidlProperties;
}
+ static @NonNull SoundTriggerModuleProperties hidl2aidlProperties(
+ @NonNull Properties hidlProperties) {
+ SoundTriggerModuleProperties aidlProperties = hidl2aidlProperties(hidlProperties.base);
+ aidlProperties.supportedModelArch = hidlProperties.supportedModelArch;
+ return aidlProperties;
+ }
+
static @NonNull
String hidl2aidlUuid(@NonNull Uuid hidlUuid) {
if (hidlUuid.node == null || hidlUuid.node.length != 6) {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
index f0a0d83..dbf91a9 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
@@ -74,4 +74,12 @@
config_2_0.data = HidlMemoryUtil.hidlMemoryToByteList(config.data);
return config_2_0;
}
+
+ static android.hardware.soundtrigger.V2_3.Properties convertProperties_2_0_to_2_3(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties) {
+ android.hardware.soundtrigger.V2_3.Properties properties_2_3 =
+ new android.hardware.soundtrigger.V2_3.Properties();
+ properties_2_3.base = properties;
+ return properties_2_3;
+ }
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
index 81252c9..2f024a5 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
@@ -16,7 +16,6 @@
package com.android.server.soundtrigger_middleware;
-import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
import android.hardware.soundtrigger.V2_3.ModelParameterRange;
import android.hidl.base.V1_0.IBase;
import android.os.IHwBinder;
@@ -54,9 +53,10 @@
*/
public interface ISoundTriggerHw2 {
/**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#getProperties(android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback
+ * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getPropertiesEx(
+ * android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getPropertiesExCallback)
*/
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Properties getProperties();
+ android.hardware.soundtrigger.V2_3.Properties getProperties();
/**
* @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel,
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index 4a852c4..3354c56 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -112,18 +112,23 @@
}
@Override
- public android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Properties getProperties() {
+ public android.hardware.soundtrigger.V2_3.Properties getProperties() {
try {
AtomicInteger retval = new AtomicInteger(-1);
- AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Properties>
+ AtomicReference<android.hardware.soundtrigger.V2_3.Properties>
properties =
new AtomicReference<>();
- as2_0().getProperties(
- (r, p) -> {
- retval.set(r);
- properties.set(p);
- });
- handleHalStatus(retval.get(), "getProperties");
+ try {
+ as2_3().getProperties_2_3(
+ (r, p) -> {
+ retval.set(r);
+ properties.set(p);
+ });
+ } catch (NotSupported e) {
+ // Fall-back to the 2.0 version:
+ return getProperties_2_0();
+ }
+ handleHalStatus(retval.get(), "getProperties_2_3");
return properties.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -312,6 +317,21 @@
return as2_0().interfaceDescriptor();
}
+ private android.hardware.soundtrigger.V2_3.Properties getProperties_2_0()
+ throws RemoteException {
+ AtomicInteger retval = new AtomicInteger(-1);
+ AtomicReference<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties>
+ properties =
+ new AtomicReference<>();
+ as2_0().getProperties(
+ (r, p) -> {
+ retval.set(r);
+ properties.set(p);
+ });
+ handleHalStatus(retval.get(), "getProperties");
+ return Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get());
+ }
+
private int loadSoundModel_2_0(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
Callback callback, int cookie)
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index 987c05f..5a06a2c 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.PermissionChecker;
import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
@@ -32,6 +33,7 @@
import android.media.soundtrigger_middleware.RecognitionStatus;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.media.soundtrigger_middleware.Status;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -223,23 +225,48 @@
}
/**
- * Throws a {@link SecurityException} if caller doesn't have the right permissions to use this
- * service.
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the right permissions to use this service.
*/
private void checkPermissions() {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.RECORD_AUDIO,
- "Caller must have the android.permission.RECORD_AUDIO permission.");
- mContext.enforceCallingOrSelfPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD,
- "Caller must have the android.permission.CAPTURE_AUDIO_HOTWORD permission.");
+ enforcePermission(Manifest.permission.RECORD_AUDIO);
+ enforcePermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD);
}
/**
- * Throws a {@link SecurityException} if caller doesn't have the right permissions to preempt
- * active sound trigger sessions.
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the right permissions to preempt active sound trigger
+ * sessions.
*/
private void checkPreemptPermissions() {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.PREEMPT_SOUND_TRIGGER,
- "Caller must have the android.permission.PREEMPT_SOUND_TRIGGER permission.");
+ enforcePermission(Manifest.permission.PREEMPT_SOUND_TRIGGER);
+ }
+
+ /**
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the given permission.
+ *
+ * @param permission The permission to check.
+ */
+ private void enforcePermission(String permission) {
+ final int status = PermissionChecker.checkCallingOrSelfPermissionForPreflight(mContext,
+ permission);
+ switch (status) {
+ case PermissionChecker.PERMISSION_GRANTED:
+ return;
+ case PermissionChecker.PERMISSION_DENIED:
+ throw new SecurityException(
+ String.format("Caller must have the %s permission.", permission));
+ case PermissionChecker.PERMISSION_DENIED_APP_OP:
+ throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
+ String.format("Caller must have the %s permission.", permission));
+ default:
+ throw new InternalServerError(
+ new RuntimeException("Unexpected perimission check result."));
+ }
}
/** State of a sound model. */
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f9ad03f..9c62e99 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2061,7 +2061,8 @@
cf.set(displayFrames.mRestricted);
}
applyStableConstraints(sysUiFl, fl, cf, displayFrames);
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
+ && adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
@@ -2138,7 +2139,8 @@
applyStableConstraints(sysUiFl, fl, cf, displayFrames);
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
+ && adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
@@ -2179,7 +2181,8 @@
cf.set(displayFrames.mContent);
df.set(displayFrames.mContent);
}
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
+ && adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index a13383d..5a591ec 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -129,6 +129,7 @@
if (win == null) {
setServerVisible(false);
mSource.setFrame(new Rect());
+ mSource.setVisibleFrame(null);
} else if (mControllable) {
mWin.setControllableInsetProvider(this);
if (mControlTarget != null) {
@@ -160,6 +161,15 @@
mTmpRect.inset(mWin.mGivenContentInsets);
}
mSource.setFrame(mTmpRect);
+
+ if (mWin.mGivenVisibleInsets.left != 0 || mWin.mGivenVisibleInsets.top != 0
+ || mWin.mGivenVisibleInsets.right != 0 || mWin.mGivenVisibleInsets.bottom != 0) {
+ mTmpRect.set(mWin.getFrameLw());
+ mTmpRect.inset(mWin.mGivenVisibleInsets);
+ mSource.setVisibleFrame(mTmpRect);
+ } else {
+ mSource.setVisibleFrame(null);
+ }
}
/**
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 1ad6e86..03969b0 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -13,6 +13,7 @@
],
srcs: [
+ ":graphicsstats_proto",
"BroadcastRadio/JavaRef.cpp",
"BroadcastRadio/NativeCallbackThread.cpp",
"BroadcastRadio/BroadcastRadioService.cpp",
@@ -103,6 +104,11 @@
"libinputflinger",
"libinputflinger_base",
"libinputservice",
+ "libprotobuf-cpp-lite",
+ "libprotoutil",
+ "libstatspull",
+ "libstatssocket",
+ "libstatslog",
"libschedulerservicehidl",
"libsensorservice",
"libsensorservicehidl",
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index d1d253b..9353fbd 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -23,6 +23,16 @@
#include <nativehelper/ScopedUtfChars.h>
#include <JankTracker.h>
#include <service/GraphicsStatsService.h>
+#include <stats_pull_atom_callback.h>
+#include <stats_event.h>
+#include <statslog.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <android/util/ProtoOutputStream.h>
+#include "android/graphics/Utils.h"
+#include "core_jni_helpers.h"
+#include "protos/graphicsstats.pb.h"
+#include <cstring>
+#include <memory>
namespace android {
@@ -77,6 +87,20 @@
GraphicsStatsService::finishDump(dump);
}
+static jlong finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr) {
+ GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+ std::vector<uint8_t>* result = new std::vector<uint8_t>();
+ GraphicsStatsService::finishDumpInMemory(dump,
+ [](void* buffer, int bufferOffset, int bufferSize, int totalSize, void* param1, void* param2) {
+ std::vector<uint8_t>* outBuffer = reinterpret_cast<std::vector<uint8_t>*>(param2);
+ if (outBuffer->size() < totalSize) {
+ outBuffer->resize(totalSize);
+ }
+ std::memcpy(outBuffer->data() + bufferOffset, buffer, bufferSize);
+ }, env, result);
+ return reinterpret_cast<jlong>(result);
+}
+
static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
ScopedByteArrayRO buffer(env, jdata);
@@ -93,19 +117,173 @@
GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
}
+static jobject gGraphicsStatsServiceObject = nullptr;
+static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID;
+
+static JNIEnv* getJNIEnv() {
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ JNIEnv* env = nullptr;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+ }
+ }
+ return env;
+}
+
+using namespace google::protobuf;
+
+// Field ids taken from FrameTimingHistogram message in atoms.proto
+#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
+#define FRAME_COUNTS_FIELD_NUMBER 2
+
+static void writeCpuHistogram(stats_event* event,
+ const uirenderer::protos::GraphicsStatsProto& stat) {
+ util::ProtoOutputStream proto;
+ for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
+ auto& bucket = stat.histogram(bucketIndex);
+ proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+ TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
+ (int)bucket.render_millis());
+ }
+ for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
+ auto& bucket = stat.histogram(bucketIndex);
+ proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+ FRAME_COUNTS_FIELD_NUMBER /* field id */,
+ (long long)bucket.frame_count());
+ }
+ std::vector<uint8_t> outVector;
+ proto.serializeToVector(&outVector);
+ stats_event_write_byte_array(event, outVector.data(), outVector.size());
+}
+
+static void writeGpuHistogram(stats_event* event,
+ const uirenderer::protos::GraphicsStatsProto& stat) {
+ util::ProtoOutputStream proto;
+ for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
+ auto& bucket = stat.gpu_histogram(bucketIndex);
+ proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+ TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
+ (int)bucket.render_millis());
+ }
+ for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
+ auto& bucket = stat.gpu_histogram(bucketIndex);
+ proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+ FRAME_COUNTS_FIELD_NUMBER /* field id */,
+ (long long)bucket.frame_count());
+ }
+ std::vector<uint8_t> outVector;
+ proto.serializeToVector(&outVector);
+ stats_event_write_byte_array(event, outVector.data(), outVector.size());
+}
+
+// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
+static bool graphicsStatsPullCallback(int32_t atom_tag, pulled_stats_event_list* data,
+ const void* cookie) {
+ JNIEnv* env = getJNIEnv();
+ if (!env) {
+ return false;
+ }
+ if (gGraphicsStatsServiceObject == nullptr) {
+ ALOGE("Failed to get graphicsstats service");
+ return false;
+ }
+
+ for (bool lastFullDay : {true, false}) {
+ jlong jdata = (jlong) env->CallLongMethod(
+ gGraphicsStatsServiceObject,
+ gGraphicsStatsService_pullGraphicsStatsMethodID,
+ (jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE));
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ ALOGE("Failed to invoke graphicsstats service");
+ return false;
+ }
+ if (!jdata) {
+ // null means data is not available for that day.
+ continue;
+ }
+ android::uirenderer::protos::GraphicsStatsServiceDumpProto serviceDump;
+ std::vector<uint8_t>* buffer = reinterpret_cast<std::vector<uint8_t>*>(jdata);
+ std::unique_ptr<std::vector<uint8_t>> bufferRelease(buffer);
+ int dataSize = buffer->size();
+ if (!dataSize) {
+ // Data is not available for that day.
+ continue;
+ }
+ io::ArrayInputStream input{buffer->data(), dataSize};
+ bool success = serviceDump.ParseFromZeroCopyStream(&input);
+ if (!success) {
+ ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'",
+ serviceDump.InitializationErrorString().c_str(), dataSize);
+ return false;
+ }
+
+ for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
+ auto& stat = serviceDump.stats(stat_index);
+ stats_event* event = add_stats_event_to_pull_data(data);
+ stats_event_set_atom_id(event, android::util::GRAPHICS_STATS);
+ stats_event_write_string8(event, stat.package_name().c_str());
+ stats_event_write_int64(event, (int64_t)stat.version_code());
+ stats_event_write_int64(event, (int64_t)stat.stats_start());
+ stats_event_write_int64(event, (int64_t)stat.stats_end());
+ stats_event_write_int32(event, (int32_t)stat.pipeline());
+ stats_event_write_int32(event, (int32_t)stat.summary().total_frames());
+ stats_event_write_int32(event, (int32_t)stat.summary().missed_vsync_count());
+ stats_event_write_int32(event, (int32_t)stat.summary().high_input_latency_count());
+ stats_event_write_int32(event, (int32_t)stat.summary().slow_ui_thread_count());
+ stats_event_write_int32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
+ stats_event_write_int32(event, (int32_t)stat.summary().slow_draw_count());
+ stats_event_write_int32(event, (int32_t)stat.summary().missed_deadline_count());
+ writeCpuHistogram(event, stat);
+ writeGpuHistogram(event, stat);
+ // TODO: fill in UI mainline module version, when the feature is available.
+ stats_event_write_int64(event, (int64_t)0);
+ stats_event_write_bool(event, !lastFullDay);
+ stats_event_build(event);
+ }
+ }
+ return true;
+}
+
+// Register a puller for GRAPHICS_STATS atom with the statsd service.
+static void nativeInit(JNIEnv* env, jobject javaObject) {
+ gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
+ pull_atom_metadata metadata = {.cool_down_ns = 10 * 1000000, // 10 milliseconds
+ .timeout_ns = 2 * NS_PER_SEC, // 2 seconds
+ .additive_fields = nullptr,
+ .additive_fields_size = 0};
+ register_stats_pull_atom_callback(android::util::GRAPHICS_STATS, &graphicsStatsPullCallback,
+ &metadata, nullptr);
+}
+
+static void nativeDestructor(JNIEnv* env, jobject javaObject) {
+ //TODO: Unregister the puller callback when a new API is available.
+ env->DeleteGlobalRef(gGraphicsStatsServiceObject);
+ gGraphicsStatsServiceObject = nullptr;
+}
+
static const JNINativeMethod sMethods[] = {
{ "nGetAshmemSize", "()I", (void*) getAshmemSize },
{ "nCreateDump", "(IZ)J", (void*) createDump },
{ "nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) addToDump },
{ "nAddToDump", "(JLjava/lang/String;)V", (void*) addFileToDump },
{ "nFinishDump", "(J)V", (void*) finishDump },
+ { "nFinishDumpInMemory", "(J)J", (void*) finishDumpInMemory },
{ "nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) saveBuffer },
+ { "nativeInit", "()V", (void*) nativeInit },
+ { "nativeDestructor", "()V", (void*)nativeDestructor }
};
int register_android_server_GraphicsStatsService(JNIEnv* env)
{
+ jclass graphicsStatsService_class = FindClassOrDie(env,
+ "com/android/server/GraphicsStatsService");
+ gGraphicsStatsService_pullGraphicsStatsMethodID = GetMethodIDOrDie(env,
+ graphicsStatsService_class, "pullGraphicsStats", "(Z)J");
return jniRegisterNativeMethods(env, "com/android/server/GraphicsStatsService",
sMethods, NELEM(sMethods));
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
index 61cd88a..c93e5c5 100644
--- a/services/net/java/android/net/ip/IpClientCallbacks.java
+++ b/services/net/java/android/net/ip/IpClientCallbacks.java
@@ -17,6 +17,7 @@
package android.net.ip;
import android.net.DhcpResults;
+import android.net.DhcpResultsParcelable;
import android.net.Layer2PacketParcelable;
import android.net.LinkProperties;
@@ -69,6 +70,18 @@
public void onNewDhcpResults(DhcpResults dhcpResults) {}
/**
+ * Callback called when new DHCP results are available.
+ *
+ * <p>This is purely advisory and not an indication of provisioning success or failure. This is
+ * only here for callers that want to expose DHCPv4 results to other APIs
+ * (e.g., WifiInfo#setInetAddress).
+ *
+ * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not
+ * the passed-in DhcpResults object is null.
+ */
+ public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {}
+
+ /**
* Indicates that provisioning was successful.
*/
public void onProvisioningSuccess(LinkProperties newLp) {}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 4d60e62..7f723b1 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -119,6 +119,7 @@
@Override
public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
mCb.onNewDhcpResults(fromStableParcelable(dhcpResults));
+ mCb.onNewDhcpResults(dhcpResults);
}
@Override
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 8632ca4..8b2f15c 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -81,7 +81,10 @@
import org.robolectric.shadows.ShadowPackageManager;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.List;
/**
@@ -1238,13 +1241,49 @@
assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber2);
}
+ /**
+ * Test that {@link UserBackupManagerService#dump()} for system user does not prefix dump with
+ * "User 0:".
+ */
+ @Test
+ public void testDump_forSystemUser_DoesNotHaveUserPrefix() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService service =
+ BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
+ UserHandle.USER_SYSTEM,
+ mContext,
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager);
+
+ StringWriter dump = new StringWriter();
+ service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]);
+
+ assertThat(dump.toString()).startsWith("Backup Manager is ");
+ }
+
+ /**
+ * Test that {@link UserBackupManagerService#dump()} for non-system user prefixes dump with
+ * "User <userid>:".
+ */
+ @Test
+ public void testDump_forNonSystemUser_HasUserPrefix() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();
+
+ StringWriter dump = new StringWriter();
+ service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]);
+
+ assertThat(dump.toString()).startsWith("User " + USER_ID + ":" + "Backup Manager is ");
+ }
+
private File createTestFile() throws IOException {
File testFile = new File(mContext.getFilesDir(), "test");
testFile.createNewFile();
return testFile;
}
-
/**
* We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
* extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 1829fb7..2fb2021 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
@@ -56,7 +57,6 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiSsid;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -182,8 +182,8 @@
}
private ScanResult createScanResult(String ssid, String bssid) {
- ScanResult result = new ScanResult();
- result.wifiSsid = WifiSsid.createFromAsciiEncoded(ssid);
+ ScanResult result = mock(ScanResult.class);
+ result.SSID = ssid;
result.BSSID = bssid;
return result;
}
@@ -794,7 +794,7 @@
@Test
public void testScanResultsScoreCacheFilter_invalidScanResults() throws Exception {
List<ScanResult> invalidScanResults = Lists.newArrayList(
- new ScanResult(),
+ mock(ScanResult.class),
createScanResult("", SCORED_NETWORK.networkKey.wifiKey.bssid),
createScanResult(WifiManager.UNKNOWN_SSID, SCORED_NETWORK.networkKey.wifiKey.bssid),
createScanResult(SSID, null),
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index 2326dfd..d44476e 100644
--- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -27,6 +27,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -59,6 +60,7 @@
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -83,6 +85,8 @@
@Mock
private UserBackupManagerService mUserBackupManagerService;
@Mock
+ private UserBackupManagerService mNonSystemUserBackupManagerService;
+ @Mock
private Context mContextMock;
@Mock
private PrintWriter mPrintWriterMock;
@@ -105,7 +109,7 @@
mUserServices = new SparseArray<>();
mUserServices.append(UserHandle.USER_SYSTEM, mUserBackupManagerService);
- mUserServices.append(NON_USER_SYSTEM, mUserBackupManagerService);
+ mUserServices.append(NON_USER_SYSTEM, mNonSystemUserBackupManagerService);
when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
@@ -512,6 +516,26 @@
mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
verifyNoMoreInteractions(mUserBackupManagerService);
+ verifyNoMoreInteractions(mNonSystemUserBackupManagerService);
+ }
+
+ /**
+ * Test that {@link BackupManagerService#dump()} dumps system user information before non-system
+ * user information.
+ */
+
+ @Test
+ public void testDump_systemUserFirst() {
+ String[] args = new String[0];
+ mService.dumpWithoutCheckingPermission(mFileDescriptorStub, mPrintWriterMock, args);
+
+ InOrder inOrder =
+ inOrder(mUserBackupManagerService, mNonSystemUserBackupManagerService);
+ inOrder.verify(mUserBackupManagerService)
+ .dump(mFileDescriptorStub, mPrintWriterMock, args);
+ inOrder.verify(mNonSystemUserBackupManagerService)
+ .dump(mFileDescriptorStub, mPrintWriterMock, args);
+ inOrder.verifyNoMoreInteractions();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index f8915c0..0f75816 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -155,7 +155,16 @@
return properties;
}
- private static void validateDefaultProperties(SoundTriggerModuleProperties properties,
+ private static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3(
+ boolean supportConcurrentCapture) {
+ android.hardware.soundtrigger.V2_3.Properties properties =
+ new android.hardware.soundtrigger.V2_3.Properties();
+ properties.base = createDefaultProperties(supportConcurrentCapture);
+ properties.supportedModelArch = "supportedModelArch";
+ return properties;
+ }
+
+ private void validateDefaultProperties(SoundTriggerModuleProperties properties,
boolean supportConcurrentCapture) {
assertEquals("implementor", properties.implementor);
assertEquals("description", properties.description);
@@ -173,8 +182,20 @@
assertEquals(supportConcurrentCapture, properties.concurrentCapture);
assertTrue(properties.triggerInEvent);
assertEquals(432, properties.powerConsumptionMw);
+
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ assertEquals("supportedModelArch", properties.supportedModelArch);
+ } else {
+ assertEquals("", properties.supportedModelArch);
+ }
}
+ private void verifyNotGetProperties() throws RemoteException {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
+ never()).getProperties(any());
+ }
+ }
private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
int hwHandle,
@@ -290,6 +311,22 @@
properties);
return null;
}).when(mHalDriver).getProperties(any());
+
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.Properties properties =
+ createDefaultProperties_2_3(
+ supportConcurrentCapture);
+ ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback)
+ invocation.getArgument(
+ 0)).onValues(0,
+ properties);
+ return null;
+ }).when(driver).getProperties_2_3(any());
+ }
+
mService = new SoundTriggerMiddlewareImpl(mHalDriver, mAudioSessionProvider);
}
@@ -716,6 +753,7 @@
SoundTriggerModuleProperties properties = allDescriptors[0].properties;
validateDefaultProperties(properties, true);
+ verifyNotGetProperties();
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index eb45960..bd7d9ec 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -20,7 +20,6 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static org.mockito.Matchers.any;
@@ -65,11 +64,10 @@
NotificationChannel updatedChannel =
new NotificationChannel("a", "", IMPORTANCE_HIGH);
- when(mConfig.getNotificationChannel(any(), anyInt(), eq("a"), eq(false)))
+ when(mConfig.getNotificationChannel(any(), anyInt(), eq("a"), eq(null), eq(false)))
.thenReturn(updatedChannel);
assertNull(extractor.process(r));
assertEquals(updatedChannel, r.getChannel());
}
-
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 172df99..ae597e3 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -112,6 +112,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -133,6 +134,7 @@
import android.testing.TestablePermissions;
import android.testing.TestableResources;
import android.text.Html;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -475,7 +477,7 @@
when(mPreferencesHelper.bubblesEnabled()).thenReturn(globalEnabled);
when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(pkgEnabled);
when(mPreferencesHelper.getNotificationChannel(
- anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ anyString(), anyInt(), anyString(), eq(null), anyBoolean())).thenReturn(
mTestNotificationChannel);
when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
mTestNotificationChannel.getImportance());
@@ -1762,7 +1764,7 @@
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testTvExtenderChannelOverride_onTv", 0,
generateNotificationRecord(null, tv).getNotification(), 0);
verify(mPreferencesHelper, times(1)).getNotificationChannel(
- anyString(), anyInt(), eq("foo"), anyBoolean());
+ anyString(), anyInt(), eq("foo"), eq(null), anyBoolean());
}
@Test
@@ -1777,7 +1779,8 @@
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testTvExtenderChannelOverride_notOnTv",
0, generateNotificationRecord(null, tv).getNotification(), 0);
verify(mPreferencesHelper, times(1)).getNotificationChannel(
- anyString(), anyInt(), eq(mTestNotificationChannel.getId()), anyBoolean());
+ anyString(), anyInt(), eq(mTestNotificationChannel.getId()), eq(null),
+ anyBoolean());
}
@Test
@@ -5044,10 +5047,9 @@
nb.build(), new UserHandle(mUid), null, 0);
// Make sure it has foreground service
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
// yes phone call, yes person, yes foreground service, but not allowed, no bubble
@@ -5802,4 +5804,79 @@
verify(mHistoryManager, times(1)).addNotification(any());
}
+
+ @Test
+ public void createConversationNotificationChannel() throws Exception {
+ NotificationChannel original = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ original.setAllowBubbles(!original.canBubble());
+ original.setShowBadge(!original.canShowBadge());
+
+ Parcel parcel = Parcel.obtain();
+ original.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ NotificationChannel orig = NotificationChannel.CREATOR.createFromParcel(parcel);
+ assertEquals(original, orig);
+ assertFalse(TextUtils.isEmpty(orig.getName()));
+
+ mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList(
+ orig)));
+
+ mBinderService.createConversationNotificationChannelForPackage(PKG, mUid, orig, "friend");
+
+ NotificationChannel friendChannel = mBinderService.getConversationNotificationChannel(
+ PKG, 0, PKG, original.getId(), "friend");
+
+ assertEquals(original.getName(), friendChannel.getName());
+ assertEquals(original.getId(), friendChannel.getParentChannelId());
+ assertEquals("friend", friendChannel.getConversationId());
+ assertEquals(null, original.getConversationId());
+ assertEquals(original.canShowBadge(), friendChannel.canShowBadge());
+ assertEquals(original.canBubble(), friendChannel.canBubble());
+ assertFalse(original.getId().equals(friendChannel.getId()));
+ assertNotNull(friendChannel.getId());
+ }
+
+ @Test
+ public void deleteConversationNotificationChannels() throws Exception {
+ NotificationChannel messagesParent =
+ new NotificationChannel("messages", "messages", IMPORTANCE_HIGH);
+ Parcel msgParcel = Parcel.obtain();
+ messagesParent.writeToParcel(msgParcel, 0);
+ msgParcel.setDataPosition(0);
+
+ NotificationChannel callsParent =
+ new NotificationChannel("calls", "calls", IMPORTANCE_HIGH);
+ Parcel callParcel = Parcel.obtain();
+ callsParent.writeToParcel(callParcel, 0);
+ callParcel.setDataPosition(0);
+
+ mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList(
+ messagesParent, callsParent)));
+
+ String conversationId = "friend";
+
+ mBinderService.createConversationNotificationChannelForPackage(
+ PKG, mUid, NotificationChannel.CREATOR.createFromParcel(msgParcel), conversationId);
+ mBinderService.createConversationNotificationChannelForPackage(
+ PKG, mUid, NotificationChannel.CREATOR.createFromParcel(callParcel),
+ conversationId);
+
+ NotificationChannel messagesChild = mBinderService.getConversationNotificationChannel(
+ PKG, 0, PKG, messagesParent.getId(), conversationId);
+ NotificationChannel callsChild = mBinderService.getConversationNotificationChannel(
+ PKG, 0, PKG, callsParent.getId(), conversationId);
+
+ assertEquals(messagesParent.getId(), messagesChild.getParentChannelId());
+ assertEquals(conversationId, messagesChild.getConversationId());
+
+ assertEquals(callsParent.getId(), callsChild.getParentChannelId());
+ assertEquals(conversationId, callsChild.getConversationId());
+
+ mBinderService.deleteConversationNotificationChannels(PKG, mUid, conversationId);
+
+ assertNull(mBinderService.getConversationNotificationChannel(
+ PKG, 0, PKG, messagesParent.getId(), conversationId));
+ assertNull(mBinderService.getConversationNotificationChannel(
+ PKG, 0, PKG, callsParent.getId(), conversationId));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8961796..7173e0c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.notification;
+import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -224,6 +225,26 @@
assertEquals(expected.getGroup(), actual.getGroup());
assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
assertEquals(expected.getLightColor(), actual.getLightColor());
+ assertEquals(expected.getParentChannelId(), actual.getParentChannelId());
+ assertEquals(expected.getConversationId(), actual.getConversationId());
+ }
+
+ private void compareChannelsParentChild(NotificationChannel parent,
+ NotificationChannel actual, String conversationId) {
+ assertEquals(parent.getName(), actual.getName());
+ assertEquals(parent.getDescription(), actual.getDescription());
+ assertEquals(parent.shouldVibrate(), actual.shouldVibrate());
+ assertEquals(parent.shouldShowLights(), actual.shouldShowLights());
+ assertEquals(parent.getImportance(), actual.getImportance());
+ assertEquals(parent.getLockscreenVisibility(), actual.getLockscreenVisibility());
+ assertEquals(parent.getSound(), actual.getSound());
+ assertEquals(parent.canBypassDnd(), actual.canBypassDnd());
+ assertTrue(Arrays.equals(parent.getVibrationPattern(), actual.getVibrationPattern()));
+ assertEquals(parent.getGroup(), actual.getGroup());
+ assertEquals(parent.getAudioAttributes(), actual.getAudioAttributes());
+ assertEquals(parent.getLightColor(), actual.getLightColor());
+ assertEquals(parent.getId(), actual.getParentChannelId());
+ assertEquals(conversationId, actual.getConversationId());
}
private void compareGroups(NotificationChannelGroup expected, NotificationChannelGroup actual) {
@@ -2786,4 +2807,40 @@
assertEquals(user10Importance, mHelper.getNotificationChannel(
pkg, uidList10[0], channelId, false).getImportance());
}
+
+ @Test
+ public void testGetConversationNotificationChannel() {
+ String conversationId = "friend";
+
+ NotificationChannel parent =
+ new NotificationChannel("parent", "messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
+
+ NotificationChannel friend = new NotificationChannel(String.format(
+ CONVERSATION_CHANNEL_ID_FORMAT, parent.getId(), conversationId),
+ "messages", IMPORTANCE_DEFAULT);
+ friend.setConversationId(parent.getId(), conversationId);
+ mHelper.createNotificationChannel(PKG_O, UID_O, friend, true, false);
+
+ compareChannelsParentChild(parent, mHelper.getNotificationChannel(
+ PKG_O, UID_O, parent.getId(), conversationId, false), conversationId);
+ }
+
+ @Test
+ public void testConversationNotificationChannelsRequireParents() {
+ String parentId = "does not exist";
+ String conversationId = "friend";
+
+ NotificationChannel friend = new NotificationChannel(String.format(
+ CONVERSATION_CHANNEL_ID_FORMAT, parentId, conversationId),
+ "messages", IMPORTANCE_DEFAULT);
+ friend.setConversationId(parentId, conversationId);
+
+ try {
+ mHelper.createNotificationChannel(PKG_O, UID_O, friend, true, false);
+ fail("allowed creation of conversation channel without a parent");
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 09ac9ce..d819b1a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -63,6 +63,26 @@
assertEquals(Insets.of(0, 100, 0, 0),
mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
false /* ignoreVisibility */));
+ assertEquals(Insets.of(0, 100, 0, 0),
+ mProvider.getSource().calculateVisibleInsets(new Rect(0, 0, 500, 500)));
+ }
+
+ @Test
+ public void testPostLayout_givenInsets() {
+ final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ ime.getFrameLw().set(0, 0, 500, 100);
+ ime.getGivenContentInsetsLw().set(0, 0, 0, 60);
+ ime.getGivenVisibleInsetsLw().set(0, 0, 0, 75);
+ ime.mHasSurface = true;
+ mProvider.setWindow(ime, null);
+ mProvider.onPostLayout();
+ assertEquals(new Rect(0, 0, 500, 40), mProvider.getSource().getFrame());
+ assertEquals(new Rect(0, 0, 500, 25), mProvider.getSource().getVisibleFrame());
+ assertEquals(Insets.of(0, 40, 0, 0),
+ mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+ assertEquals(Insets.of(0, 25, 0, 0),
+ mProvider.getSource().calculateVisibleInsets(new Rect(0, 0, 500, 500)));
}
@Test
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 198b4c3..bde2cfd 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -611,65 +611,91 @@
int setParameter(UUID modelId, @ModelParams int modelParam, int value) {
synchronized (mLock) {
- MetricsLogger.count(mContext, "sth_set_parameter", 1);
- if (modelId == null || mModule == null) {
- return SoundTrigger.STATUS_ERROR;
- }
- ModelData modelData = mModelDataMap.get(modelId);
- if (modelData == null) {
- Slog.w(TAG, "SetParameter: Invalid model id:" + modelId);
- return SoundTrigger.STATUS_BAD_VALUE;
- }
- if (!modelData.isModelLoaded()) {
- Slog.i(TAG, "SetParameter: Given model is not loaded:" + modelId);
- return SoundTrigger.STATUS_BAD_VALUE;
- }
-
- return mModule.setParameter(modelData.getHandle(), modelParam, value);
+ return setParameterLocked(mModelDataMap.get(modelId), modelParam, value);
}
}
- int getParameter(@NonNull UUID modelId, @ModelParams int modelParam)
- throws UnsupportedOperationException, IllegalArgumentException {
+ int setKeyphraseParameter(int keyphraseId, @ModelParams int modelParam, int value) {
synchronized (mLock) {
- MetricsLogger.count(mContext, "sth_get_parameter", 1);
- if (mModule == null) {
- throw new UnsupportedOperationException("SoundTriggerModule not initialized");
- }
-
- ModelData modelData = mModelDataMap.get(modelId);
- if (modelData == null) {
- throw new IllegalArgumentException("Invalid model id:" + modelId);
- }
- if (!modelData.isModelLoaded()) {
- throw new UnsupportedOperationException("Given model is not loaded:" + modelId);
- }
-
- return mModule.getParameter(modelData.getHandle(), modelParam);
+ return setParameterLocked(getKeyphraseModelDataLocked(keyphraseId), modelParam, value);
}
}
+ private int setParameterLocked(@Nullable ModelData modelData, @ModelParams int modelParam,
+ int value) {
+ MetricsLogger.count(mContext, "sth_set_parameter", 1);
+ if (mModule == null) {
+ return SoundTrigger.STATUS_NO_INIT;
+ }
+ if (modelData == null || !modelData.isModelLoaded()) {
+ Slog.i(TAG, "SetParameter: Given model is not loaded:" + modelData);
+ return SoundTrigger.STATUS_BAD_VALUE;
+ }
+
+ return mModule.setParameter(modelData.getHandle(), modelParam, value);
+ }
+
+ int getParameter(@NonNull UUID modelId, @ModelParams int modelParam) {
+ synchronized (mLock) {
+ return getParameterLocked(mModelDataMap.get(modelId), modelParam);
+ }
+ }
+
+ int getKeyphraseParameter(int keyphraseId, @ModelParams int modelParam) {
+ synchronized (mLock) {
+ return getParameterLocked(getKeyphraseModelDataLocked(keyphraseId), modelParam);
+ }
+ }
+
+ private int getParameterLocked(@Nullable ModelData modelData, @ModelParams int modelParam) {
+ MetricsLogger.count(mContext, "sth_get_parameter", 1);
+ if (mModule == null) {
+ throw new UnsupportedOperationException("SoundTriggerModule not initialized");
+ }
+
+ if (modelData == null) {
+ throw new IllegalArgumentException("Invalid model id");
+ }
+ if (!modelData.isModelLoaded()) {
+ throw new UnsupportedOperationException("Given model is not loaded:" + modelData);
+ }
+
+ return mModule.getParameter(modelData.getHandle(), modelParam);
+ }
+
@Nullable
ModelParamRange queryParameter(@NonNull UUID modelId, @ModelParams int modelParam) {
synchronized (mLock) {
- MetricsLogger.count(mContext, "sth_query_parameter", 1);
- if (mModule == null) {
- return null;
- }
- ModelData modelData = mModelDataMap.get(modelId);
- if (modelData == null) {
- Slog.w(TAG, "queryParameter: Invalid model id:" + modelId);
- return null;
- }
- if (!modelData.isModelLoaded()) {
- Slog.i(TAG, "queryParameter: Given model is not loaded:" + modelId);
- return null;
- }
-
- return mModule.queryParameter(modelData.getHandle(), modelParam);
+ return queryParameterLocked(mModelDataMap.get(modelId), modelParam);
}
}
+ @Nullable
+ ModelParamRange queryKeyphraseParameter(int keyphraseId, @ModelParams int modelParam) {
+ synchronized (mLock) {
+ return queryParameterLocked(getKeyphraseModelDataLocked(keyphraseId), modelParam);
+ }
+ }
+
+ @Nullable
+ private ModelParamRange queryParameterLocked(@Nullable ModelData modelData,
+ @ModelParams int modelParam) {
+ MetricsLogger.count(mContext, "sth_query_parameter", 1);
+ if (mModule == null) {
+ return null;
+ }
+ if (modelData == null) {
+ Slog.w(TAG, "queryParameter: Invalid model id");
+ return null;
+ }
+ if (!modelData.isModelLoaded()) {
+ Slog.i(TAG, "queryParameter: Given model is not loaded:" + modelData);
+ return null;
+ }
+
+ return mModule.queryParameter(modelData.getHandle(), modelParam);
+ }
+
//---- SoundTrigger.StatusListener methods
@Override
public void onRecognition(RecognitionEvent event) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
index d05e044..54dffdc 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
@@ -16,17 +16,17 @@
package com.android.server.soundtrigger;
+import android.annotation.Nullable;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.ModelParams;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
-import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
-import android.hardware.soundtrigger.SoundTriggerModule;
+
+import com.android.server.voiceinteraction.VoiceInteractionManagerService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -71,6 +71,58 @@
public abstract ModuleProperties getModuleProperties();
/**
+ * Set a model specific {@link ModelParams} with the given value. This
+ * parameter will keep its value for the duration the model is loaded regardless of starting and
+ * stopping recognition. Once the model is unloaded, the value will be lost.
+ * {@link SoundTriggerInternal#queryParameter} should be checked first before calling this
+ * method.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * to modify model parameters
+ * @param modelParam {@link ModelParams}
+ * @param value Value to set
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+ * if API is not supported by HAL
+ */
+ public abstract int setParameter(int keyphraseId, @ModelParams int modelParam, int value);
+
+ /**
+ * Get a model specific {@link ModelParams}. This parameter will keep its value
+ * for the duration the model is loaded regardless of starting and stopping recognition.
+ * Once the model is unloaded, the value will be lost. If the value is not set, a default
+ * value is returned. See ModelParams for parameter default values.
+ * {@link SoundTriggerInternal#queryParameter} should be checked first before calling this
+ * method.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * to modify model parameters
+ * @param modelParam {@link ModelParams}
+ * @return value of parameter
+ * @throws UnsupportedOperationException if hal or model do not support this API.
+ * queryParameter should be checked first.
+ * @throws IllegalArgumentException if invalid model handle or parameter is passed.
+ * queryParameter should be checked first.
+ */
+ public abstract int getParameter(int keyphraseId, @ModelParams int modelParam);
+
+ /**
+ * Determine if parameter control is supported for the given model handle.
+ * This method should be checked prior to calling {@link SoundTriggerInternal#setParameter}
+ * or {@link SoundTriggerInternal#getParameter}.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * to modify model parameters
+ * @param modelParam {@link ModelParams}
+ * @return supported range of parameter, null if not supported
+ */
+ @Nullable
+ public abstract ModelParamRange queryParameter(int keyphraseId,
+ @ModelParams int modelParam);
+
+ /**
* Unloads (and stops if running) the given keyphraseId
*/
public abstract int unloadKeyphraseModel(int keyphaseId);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 68b16f3..e37755b 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -1446,26 +1446,45 @@
@Override
public int startRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
IRecognitionStatusCallback listener, RecognitionConfig recognitionConfig) {
- if (!isInitialized()) return STATUS_ERROR;
+ if (!isInitialized()) throw new UnsupportedOperationException();
return mSoundTriggerHelper.startKeyphraseRecognition(keyphraseId, soundModel, listener,
recognitionConfig);
}
@Override
public synchronized int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
- if (!isInitialized()) return STATUS_ERROR;
+ if (!isInitialized()) throw new UnsupportedOperationException();
return mSoundTriggerHelper.stopKeyphraseRecognition(keyphraseId, listener);
}
@Override
public ModuleProperties getModuleProperties() {
- if (!isInitialized()) return null;
+ if (!isInitialized()) throw new UnsupportedOperationException();
return mSoundTriggerHelper.getModuleProperties();
}
@Override
+ public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) {
+ if (!isInitialized()) throw new UnsupportedOperationException();
+ return mSoundTriggerHelper.setKeyphraseParameter(keyphraseId, modelParam, value);
+ }
+
+ @Override
+ public int getParameter(int keyphraseId, @ModelParams int modelParam) {
+ if (!isInitialized()) throw new UnsupportedOperationException();
+ return mSoundTriggerHelper.getKeyphraseParameter(keyphraseId, modelParam);
+ }
+
+ @Override
+ @Nullable
+ public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) {
+ if (!isInitialized()) throw new UnsupportedOperationException();
+ return mSoundTriggerHelper.queryKeyphraseParameter(keyphraseId, modelParam);
+ }
+
+ @Override
public int unloadKeyphraseModel(int keyphraseId) {
- if (!isInitialized()) return STATUS_ERROR;
+ if (!isInitialized()) throw new UnsupportedOperationException();
return mSoundTriggerHelper.unloadKeyphraseSoundModel(keyphraseId);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index e9db31b..06c8074 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -41,7 +41,9 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.ModelParams;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.os.Binder;
@@ -1084,6 +1086,55 @@
}
}
+ @Override
+ public int setParameter(IVoiceInteractionService service, int keyphraseId,
+ @ModelParams int modelParam, int value) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService(service);
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSoundTriggerInternal.setParameter(keyphraseId, modelParam, value);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+
+ @Override
+ public int getParameter(IVoiceInteractionService service, int keyphraseId,
+ @ModelParams int modelParam) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService(service);
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSoundTriggerInternal.getParameter(keyphraseId, modelParam);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+
+ @Override
+ @Nullable
+ public ModelParamRange queryParameter(IVoiceInteractionService service,
+ int keyphraseId, @ModelParams int modelParam) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService(service);
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSoundTriggerInternal.queryParameter(keyphraseId, modelParam);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+
private synchronized void unloadAllKeyphraseModels() {
for (int i = 0; i < mLoadedKeyphraseIds.size(); i++) {
final long caller = Binder.clearCallingIdentity();
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index 368f8f1..97bcbc0 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -18,11 +18,13 @@
import android.annotation.Nullable;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.provider.Settings;
import com.android.telephony.Rlog;
@@ -76,7 +78,7 @@
*/
public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
IPackageManager packageManager, IPermissionManager permissionManager,
- TelephonyManager telephonyManager, ContentResolver contentResolver, int userId) {
+ TelephonyManager telephonyManager, int userId, Context context) {
if (DEBUG) {
Rlog.d(TAG, "disableCarrierAppsUntilPrivileged");
}
@@ -85,6 +87,7 @@
config.getDisabledUntilUsedPreinstalledCarrierApps();
ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+ ContentResolver contentResolver = getContentResolverForUser(context, userId);
disableCarrierAppsUntilPrivileged(callingPackage, packageManager, permissionManager,
telephonyManager, contentResolver, userId, systemCarrierAppsDisabledUntilUsed,
systemCarrierAssociatedAppsDisabledUntilUsed);
@@ -102,8 +105,8 @@
* Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
*/
public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
- IPackageManager packageManager, IPermissionManager permissionManager,
- ContentResolver contentResolver, int userId) {
+ IPackageManager packageManager, IPermissionManager permissionManager, int userId,
+ Context context) {
if (DEBUG) {
Rlog.d(TAG, "disableCarrierAppsUntilPrivileged");
}
@@ -114,15 +117,23 @@
ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+ ContentResolver contentResolver = getContentResolverForUser(context, userId);
disableCarrierAppsUntilPrivileged(callingPackage, packageManager, permissionManager,
null /* telephonyManager */, contentResolver, userId,
systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed);
}
+ private static ContentResolver getContentResolverForUser(Context context, int userId) {
+ Context userContext = context.createContextAsUser(UserHandle.getUserHandleForUid(userId),
+ 0);
+ return userContext.getContentResolver();
+ }
+
/**
* Disable carrier apps until they are privileged
* Must be public b/c framework unit tests can't access package-private methods.
*/
+ // Must be public b/c framework unit tests can't access package-private methods.
@VisibleForTesting
public static void disableCarrierAppsUntilPrivileged(String callingPackage,
IPackageManager packageManager, IPermissionManager permissionManager,
@@ -142,9 +153,8 @@
systemCarrierAssociatedAppsDisabledUntilUsed);
List<String> enabledCarrierPackages = new ArrayList<>();
-
- boolean hasRunOnce = Settings.Secure.getIntForUser(
- contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 0, userId) == 1;
+ boolean hasRunOnce = Settings.Secure.getInt(contentResolver,
+ Settings.Secure.CARRIER_APPS_HANDLED, 0) == 1;
try {
for (ApplicationInfo ai : candidates) {
@@ -259,8 +269,7 @@
// Mark the execution so we do not disable apps again.
if (!hasRunOnce) {
- Settings.Secure.putIntForUser(
- contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1, userId);
+ Settings.Secure.putInt(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1);
}
if (!enabledCarrierPackages.isEmpty()) {
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 1193199..2d0bd52 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -53,8 +53,7 @@
/**
* Create a new CellLocation from a intent notifier Bundle
*
- * This method is used by PhoneStateIntentReceiver and maybe by
- * external applications.
+ * This method maybe used by external applications.
*
* @param bundle Bundle from intent notifier
* @return newly created CellLocation
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 1f7d55f..1c58f8f 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -87,8 +87,7 @@
/**
* Create a new SignalStrength from a intent notifier Bundle
*
- * This method is used by PhoneStateIntentReceiver and maybe by
- * external applications.
+ * This method may be used by external applications.
*
* @param m Bundle from intent notifier
* @return newly created SignalStrength
diff --git a/telephony/java/android/telephony/VoLteServiceState.java b/telephony/java/android/telephony/VoLteServiceState.java
index 1214012..d4a27d9 100644
--- a/telephony/java/android/telephony/VoLteServiceState.java
+++ b/telephony/java/android/telephony/VoLteServiceState.java
@@ -53,8 +53,7 @@
/**
* Create a new VoLteServiceState from a intent notifier Bundle
*
- * This method is used by PhoneStateIntentReceiver and maybe by
- * external applications.
+ * This method is maybe used by external applications.
*
* @param m Bundle from intent notifier
* @return newly created VoLteServiceState
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index f78c65f..54c07cf 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -123,32 +123,6 @@
public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED
= TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED;
- /**
- * Broadcast Action: The phone's signal strength has changed. The intent will have the
- * following extra values:</p>
- * <ul>
- * <li><em>phoneName</em> - A string version of the phone name.</li>
- * <li><em>asu</em> - A numeric value for the signal strength.
- * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu).
- * The following special values are defined:
- * <ul><li>0 means "-113 dBm or less".</li><li>31 means "-51 dBm or greater".</li></ul>
- * </li>
- * </ul>
- *
- * <p class="note">
- * You can <em>not</em> receive this through components declared
- * in manifests, only by exlicitly registering for it with
- * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,
- * android.content.IntentFilter) Context.registerReceiver()}.
- *
- * <p class="note">
- * Requires the READ_PHONE_STATE permission.
- *
- * <p class="note">This is a protected intent that can only be sent
- * by the system.
- */
- public static final String ACTION_SIGNAL_STRENGTH_CHANGED = "android.intent.action.SIG_STR";
-
/**
* Broadcast Action: The data connection state has changed for any one of the
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index 46105f4..0b2077d 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -149,7 +149,12 @@
The package name of the class containing the field/method.
"""
full_class_name = signature.split(";->")[0]
- package_name = full_class_name[1:full_class_name.rindex("/")]
+ # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
+ if (full_class_name[0] != "L"):
+ raise ValueError("Expected to start with 'L': %s" % full_class_name)
+ full_class_name = full_class_name[1:]
+ # If full_class_name doesn't contain '/', then package_name will be ''.
+ package_name = full_class_name.rpartition("/")[0]
return package_name.replace('/', '.')
class FlagsDict:
diff --git a/tools/lock_agent/Android.bp b/tools/lock_agent/Android.bp
index 79dce4a..7b2ca9a 100644
--- a/tools/lock_agent/Android.bp
+++ b/tools/lock_agent/Android.bp
@@ -25,6 +25,7 @@
srcs: ["agent.cpp"],
static_libs: [
"libbase",
+ "liblog",
"libz",
"slicer",
],
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 09d5386..180368c 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -67,10 +67,15 @@
optimize: {
enabled: false
},
+ hostdex: true, // for hiddenapi check
visibility: [
"//frameworks/base", // TODO(b/140299412) remove once all dependencies are fixed
"//frameworks/opt/net/wifi/service:__subpackages__",
] + test_access_hidden_api_whitelist,
+ apex_available: [
+ "com.android.wifi",
+ "test_com.android.wifi",
+ ],
plugins: ["java_api_finder"],
}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 71942f0..f490766 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -250,4 +250,6 @@
void unregisterSuggestionConnectionStatusListener(int listenerIdentifier, String packageName);
int calculateSignalLevel(int rssi);
+
+ List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(in List<ScanResult> scanResults);
}
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 65e9b79..2b7f8af 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -182,6 +182,12 @@
private final @SecurityType int mSecurityType;
/**
+ * Delay in milliseconds before shutting down soft AP when
+ * there are no connected devices.
+ */
+ private final int mShutdownTimeoutMillis;
+
+ /**
* Security types we support.
*/
/** @hide */
@@ -213,7 +219,7 @@
/** Private constructor for Builder and Parcelable implementation. */
private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
@Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
- @SecurityType int securityType, int maxNumberOfClients) {
+ @SecurityType int securityType, int maxNumberOfClients, int shutdownTimeoutMillis) {
mSsid = ssid;
mBssid = bssid;
mPassphrase = passphrase;
@@ -222,6 +228,7 @@
mChannel = channel;
mSecurityType = securityType;
mMaxNumberOfClients = maxNumberOfClients;
+ mShutdownTimeoutMillis = shutdownTimeoutMillis;
}
@Override
@@ -240,13 +247,14 @@
&& mBand == other.mBand
&& mChannel == other.mChannel
&& mSecurityType == other.mSecurityType
- && mMaxNumberOfClients == other.mMaxNumberOfClients;
+ && mMaxNumberOfClients == other.mMaxNumberOfClients
+ && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis;
}
@Override
public int hashCode() {
return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
- mBand, mChannel, mSecurityType, mMaxNumberOfClients);
+ mBand, mChannel, mSecurityType, mMaxNumberOfClients, mShutdownTimeoutMillis);
}
@Override
@@ -261,6 +269,7 @@
sbuf.append(" \n Channel =").append(mChannel);
sbuf.append(" \n SecurityType=").append(getSecurityType());
sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients);
+ sbuf.append(" \n ShutdownTimeoutMillis=").append(mShutdownTimeoutMillis);
return sbuf.toString();
}
@@ -274,6 +283,7 @@
dest.writeInt(mChannel);
dest.writeInt(mSecurityType);
dest.writeInt(mMaxNumberOfClients);
+ dest.writeInt(mShutdownTimeoutMillis);
}
@Override
@@ -289,7 +299,7 @@
in.readString(),
in.readParcelable(MacAddress.class.getClassLoader()),
in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
- in.readInt());
+ in.readInt(), in.readInt());
}
@Override
@@ -316,19 +326,6 @@
return mBssid;
}
- // TODO: Remove it after update the caller
- /**
- * Returns String set to be passphrase for the WPA2-PSK AP.
- * {@link #setWpa2Passphrase(String)}.
- */
- @Nullable
- public String getWpa2Passphrase() {
- if (mSecurityType == SECURITY_TYPE_WPA2_PSK) {
- return mPassphrase;
- }
- return null;
- }
-
/**
* Returns String set to be passphrase for current AP.
* {@link #setPassphrase(String, @SecurityType int)}.
@@ -381,6 +378,15 @@
}
/**
+ * Returns the shutdown timeout in milliseconds.
+ * The Soft AP will shutdown when there are no devices associated to it for
+ * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(int)}.
+ */
+ public int getShutdownTimeoutMillis() {
+ return mShutdownTimeoutMillis;
+ }
+
+ /**
* Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
* Soft AP.
*
@@ -396,6 +402,7 @@
private int mChannel;
private int mMaxNumberOfClients;
private int mSecurityType;
+ private int mShutdownTimeoutMillis;
/**
* Constructs a Builder with default values (see {@link Builder}).
@@ -409,6 +416,7 @@
mChannel = 0;
mMaxNumberOfClients = 0;
mSecurityType = SECURITY_TYPE_OPEN;
+ mShutdownTimeoutMillis = 0;
}
/**
@@ -425,6 +433,7 @@
mChannel = other.mChannel;
mMaxNumberOfClients = other.mMaxNumberOfClients;
mSecurityType = other.mSecurityType;
+ mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
}
/**
@@ -435,7 +444,8 @@
@NonNull
public SoftApConfiguration build() {
return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
- mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients);
+ mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
+ mShutdownTimeoutMillis);
}
/**
@@ -482,22 +492,6 @@
return this;
}
- // TODO: Remove it after update the caller
- /**
- * Specifies that this AP should use WPA2-PSK with the given ASCII WPA2 passphrase.
- * When set to null, an open network is created.
- * <p>
- *
- * @param passphrase The passphrase to use, or null to unset a previously-set WPA2-PSK
- * configuration.
- * @return Builder for chaining.
- * @throws IllegalArgumentException when the passphrase is the empty string
- */
- @NonNull
- public Builder setWpa2Passphrase(@Nullable String passphrase) {
- return setPassphrase(passphrase, SECURITY_TYPE_WPA2_PSK);
- }
-
/**
* Specifies that this AP should use specific security type with the given ASCII passphrase.
*
@@ -643,5 +637,30 @@
mMaxNumberOfClients = maxNumberOfClients;
return this;
}
+
+ /**
+ * Specifies the shutdown timeout in milliseconds.
+ * The Soft AP will shut down when there are no devices connected to it for
+ * the timeout duration.
+ *
+ * Specify a value of 0 to have the framework automatically use default timeout
+ * setting which defined in {@link R.integer.config_wifi_framework_soft_ap_timeout_delay}
+ *
+ * <p>
+ * <li>If not set, defaults to 0</li>
+ * <li>The shut down timout will apply when
+ * {@link Settings.Global.SOFT_AP_TIMEOUT_ENABLED} is true</li>
+ *
+ * @param timeoutMillis milliseconds of the timeout delay.
+ * @return Builder for chaining.
+ */
+ @NonNull
+ public Builder setShutdownTimeoutMillis(int timeoutMillis) {
+ if (timeoutMillis < 0) {
+ throw new IllegalArgumentException("Invalid timeout value");
+ }
+ mShutdownTimeoutMillis = timeoutMillis;
+ return this;
+ }
}
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index e870481..1baab12 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1366,6 +1366,36 @@
}
/**
+ * Retrieve a list of {@link WifiConfiguration} for available {@link WifiNetworkSuggestion}
+ * matching the given list of {@link ScanResult}.
+ *
+ * An available {@link WifiNetworkSuggestion} must satisfy:
+ * <ul>
+ * <li> Matching one of the {@link ScanResult} from the given list.
+ * <li> and {@link WifiNetworkSuggestion.Builder#setIsUserAllowedToManuallyConnect(boolean)} set
+ * to true.
+ * </ul>
+ *
+ * @param scanResults a list of scanResult.
+ * @return a list of @link WifiConfiguration} for available {@link WifiNetworkSuggestion}
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD
+ })
+ @NonNull
+ public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(
+ @NonNull List<ScanResult> scanResults) {
+ try {
+ return mService.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(scanResults);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Returns a list of unique Hotspot 2.0 OSU (Online Sign-Up) providers associated with a given
* list of ScanResult.
*
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index d91efbc..3c13562d 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -589,4 +589,10 @@
public int calculateSignalLevel(int rssi) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(
+ List<ScanResult> scanResults) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index acd3343..eeea7e2 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -117,6 +117,7 @@
.setChannel(149, SoftApConfiguration.BAND_5GHZ)
.setHiddenSsid(true)
.setMaxNumberOfClients(10)
+ .setShutdownTimeoutMillis(500000)
.build();
assertThat(original.getPassphrase()).isEqualTo("secretsecret");
assertThat(original.getSecurityType()).isEqualTo(
@@ -125,6 +126,7 @@
assertThat(original.getChannel()).isEqualTo(149);
assertThat(original.isHiddenSsid()).isEqualTo(true);
assertThat(original.getMaxNumberOfClients()).isEqualTo(10);
+ assertThat(original.getShutdownTimeoutMillis()).isEqualTo(500000);
SoftApConfiguration unparceled = parcelUnparcel(original);
assertThat(unparceled).isNotSameAs(original);
@@ -230,4 +232,10 @@
.build();
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalieShutdownTimeoutMillis() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setShutdownTimeoutMillis(-1)
+ .build();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 5ac50a0..4b83718 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2185,4 +2185,18 @@
result = WifiManager.parseDppChannelList(channelList);
assertEquals(result.size(), 0);
}
+
+ /**
+ * Test getWifiConfigsForMatchedNetworkSuggestions for given scanResults.
+ */
+ @Test
+ public void testGetWifiConfigsForMatchedNetworkSuggestions() throws Exception {
+ List<WifiConfiguration> testResults = new ArrayList<>();
+ testResults.add(new WifiConfiguration());
+
+ when(mWifiService.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(any(List.class)))
+ .thenReturn(testResults);
+ assertEquals(testResults, mWifiManager
+ .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(new ArrayList<>()));
+ }
}