Merge "Fix race condition btwn row and shelf icon init" into rvc-dev
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 3dbe413..0617eb6 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -116,7 +116,6 @@
"libcutils",
"libgtest_prod",
"libprotoutil",
- "libstatsmetadata",
"libstatslog_statsd",
"libsysutils",
"libutils",
@@ -129,51 +128,6 @@
],
}
-// ================
-// libstatsmetadata
-// ================
-
-genrule {
- name: "atoms_info.h",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --atomsInfoHeader $(genDir)/atoms_info.h",
- out: [
- "atoms_info.h",
- ],
-}
-
-genrule {
- name: "atoms_info.cpp",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --atomsInfoCpp $(genDir)/atoms_info.cpp",
- out: [
- "atoms_info.cpp",
- ],
-}
-
-cc_library_static {
- name: "libstatsmetadata",
- host_supported: true,
- generated_sources: [
- "atoms_info.cpp",
- ],
- generated_headers: [
- "atoms_info.h",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- export_generated_headers: [
- "atoms_info.h",
- ],
- apex_available: [
- //TODO(b/149782403): Remove this once statsd no longer links against libstatsmetadata
- "com.android.os.statsd",
- "test_com.android.os.statsd",
- ],
-}
-
genrule {
name: "statslog_statsd.h",
tools: ["stats-log-api-gen"],
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 8527185..ff5717e 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -110,8 +110,6 @@
optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC];
- optional bool allow_from_any_uid = 50003 [default = false];
-
repeated string module = 50004;
optional bool truncate_timestamp = 50005 [default = false];
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index dc20a02..a5f0ac9 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -145,8 +145,7 @@
PacketWakeupOccurred packet_wakeup_occurred = 44 [(module) = "framework"];
WallClockTimeShifted wall_clock_time_shifted = 45 [(module) = "framework"];
AnomalyDetected anomaly_detected = 46 [(module) = "statsd"];
- AppBreadcrumbReported app_breadcrumb_reported =
- 47 [(allow_from_any_uid) = true, (module) = "statsd"];
+ AppBreadcrumbReported app_breadcrumb_reported = 47 [(module) = "statsd"];
AppStartOccurred app_start_occurred = 48 [(module) = "framework", (module) = "statsdtest"];
AppStartCanceled app_start_canceled = 49 [(module) = "framework"];
AppStartFullyDrawn app_start_fully_drawn = 50 [(module) = "framework"];
@@ -157,7 +156,7 @@
AppStartMemoryStateCaptured app_start_memory_state_captured = 55 [(module) = "framework"];
ShutdownSequenceReported shutdown_sequence_reported = 56 [(module) = "framework"];
BootSequenceReported boot_sequence_reported = 57;
- DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true, (module) = "statsd"];
+ DaveyOccurred davey_occurred = 58 [(module) = "statsd"];
OverlayStateChanged overlay_state_changed =
59 [(module) = "framework", (module) = "statsdtest"];
ForegroundServiceStateChanged foreground_service_state_changed
@@ -186,8 +185,7 @@
WTFOccurred wtf_occurred = 80 [(module) = "framework"];
LowMemReported low_mem_reported = 81 [(module) = "framework"];
GenericAtom generic_atom = 82;
- KeyValuePairsAtom key_value_pairs_atom =
- 83 [(allow_from_any_uid) = true, (module) = "framework", (module) = "statsd"];
+ KeyValuePairsAtom key_value_pairs_atom = 83 [(module) = "framework", (module) = "statsd"];
VibratorStateChanged vibrator_state_changed = 84 [(module) = "framework"];
DeferredJobStatsReported deferred_job_stats_reported = 85 [(module) = "framework"];
ThermalThrottlingStateChanged thermal_throttling = 86 [deprecated=true];
@@ -317,7 +315,7 @@
AssistGestureFeedbackReported assist_gesture_feedback_reported = 175 [(module) = "sysui"];
AssistGestureProgressReported assist_gesture_progress_reported = 176 [(module) = "sysui"];
TouchGestureClassified touch_gesture_classified = 177 [(module) = "framework"];
- HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true, (module) = "framework"];
+ HiddenApiUsed hidden_api_used = 178 [(module) = "framework"];
StyleUIChanged style_ui_changed = 179 [(module) = "sysui"];
PrivacyIndicatorsInteracted privacy_indicators_interacted =
180 [(module) = "permissioncontroller"];
@@ -383,7 +381,7 @@
UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226;
CameraActionEvent camera_action_event = 227 [(module) = "framework"];
AppCompatibilityChangeReported app_compatibility_change_reported =
- 228 [(allow_from_any_uid) = true, (module) = "framework"];
+ 228 [(module) = "framework"];
PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"];
VmsClientConnectionStateChanged vms_client_connection_state_changed =
230 [(module) = "car"];
@@ -486,6 +484,7 @@
KeystoreKeyEventReported keystore_key_event_reported = 302;
NetworkTetheringReported network_tethering_reported =
303 [(module) = "network_tethering"];
+ ImeTouchReported ime_touch_reported = 304 [(module) = "sysui"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -3061,6 +3060,18 @@
}
/**
+ * Logs when IME is on.
+ *
+ * Logged from: /packages/SystemUI/src/com/android/systemui/
+ statusbar/phone/NavigationBarView.java
+ *
+ */
+message ImeTouchReported {
+ optional int32 x_coordinate = 1; // X coordinate for ACTION_DOWN event.
+ optional int32 y_coordinate = 2; // Y coordinate for ACTION_DOWN event.
+}
+
+/**
* Logs when Launcher (HomeScreen) UI has changed or was interacted.
*
* Logged from:
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 7e825ef..60de1a2 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -21,7 +21,6 @@
#include <private/android_filesystem_config.h>
#include "CountMetricProducer.h"
-#include "atoms_info.h"
#include "condition/CombinationConditionTracker.h"
#include "condition/SimpleConditionTracker.h"
#include "guardrail/StatsdStats.h"
@@ -372,13 +371,6 @@
bool MetricsManager::checkLogCredentials(const LogEvent& event) {
- // TODO(b/154856835): Remove this check once we get whitelist from the config.
- if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) !=
- android::util::AtomsInfo::kWhitelistedAtoms.end())
- {
- return true;
- }
-
if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) {
return true;
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 22a63d9..3772755 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2816,6 +2816,11 @@
* The system may disallow entering picture-in-picture in various cases, including when the
* activity is not visible, if the screen is locked or if the user has an activity pinned.
*
+ * <p>By default, system calculates the dimension of picture-in-picture window based on the
+ * given {@param params}.
+ * See <a href="{@docRoot}guide/topics/ui/picture-in-picture">Picture-in-picture Support</a>
+ * on how to override this behavior.</p>
+ *
* @see android.R.attr#supportsPictureInPicture
* @see PictureInPictureParams
*
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index d6e7762..fc8248e 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -41,6 +41,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.telephony.TelephonyManager;
import android.util.DataUnit;
import android.util.Log;
@@ -198,6 +199,12 @@
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ * <p>Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
@@ -231,6 +238,12 @@
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ * <p>Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
@@ -268,6 +281,12 @@
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ * <p>Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
@@ -301,7 +320,7 @@
/**
* Query network usage statistics details for a given uid.
*
- * #see queryDetailsForUidTagState(int, String, long, long, int, int, int)
+ * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
long startTime, long endTime, int uid) throws SecurityException {
@@ -319,7 +338,7 @@
/**
* Query network usage statistics details for a given uid and tag.
*
- * #see queryDetailsForUidTagState(int, String, long, long, int, int, int)
+ * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
long startTime, long endTime, int uid, int tag) throws SecurityException {
@@ -344,6 +363,12 @@
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ * <p>Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
@@ -398,6 +423,12 @@
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ * <p>Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
@@ -455,7 +486,7 @@
/**
* Registers to receive notifications about data usage on specified networks.
*
- * #see registerUsageCallback(int, String[], long, UsageCallback, Handler)
+ * @see #registerUsageCallback(int, String, long, UsageCallback, Handler)
*/
public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
UsageCallback callback) {
@@ -472,6 +503,12 @@
* @param networkType Type of network to monitor. Either
{@link ConnectivityManager#TYPE_MOBILE} or {@link ConnectivityManager#TYPE_WIFI}.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ * <p>Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when registering for the mobile network type to receive
+ * notifications for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param thresholdBytes Threshold in bytes to be notified on.
* @param callback The {@link UsageCallback} that the system will call when data usage
* has exceeded the specified threshold.
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 2ce6a86..8a6cc95 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -154,6 +154,7 @@
* been misbehaving in some manner.
* Apps in this bucket will have the most restrictions, including network restrictions and
* additional restrictions on jobs.
+ * <p> Note: this bucket is not enabled in {@link Build.VERSION_CODES#R}.
* @see #getAppStandbyBucket()
*/
public static final int STANDBY_BUCKET_RESTRICTED = 45;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 772845d..8d65c92 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -84,6 +84,8 @@
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
@@ -189,6 +191,9 @@
// Only show an annoying dialog at most every 30 seconds
private static final long MIN_DIALOG_INTERVAL_MS = 30000;
+ // Only log a dropbox entry at most every 30 seconds
+ private static final long MIN_DROPBOX_INTERVAL_MS = 3000;
+
// How many Span tags (e.g. animations) to report.
private static final int MAX_SPAN_TAGS = 20;
@@ -1752,16 +1757,20 @@
// Not perfect, but fast and good enough for dup suppression.
Integer crashFingerprint = info.hashCode();
long lastViolationTime = 0;
- if (mLastViolationTime != null) {
- Long vtime = mLastViolationTime.get(crashFingerprint);
- if (vtime != null) {
- lastViolationTime = vtime;
- }
- } else {
- mLastViolationTime = new ArrayMap<>(1);
- }
long now = SystemClock.uptimeMillis();
- mLastViolationTime.put(crashFingerprint, now);
+ if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
+ if (mLastViolationTime != null) {
+ Long vtime = mLastViolationTime.get(crashFingerprint);
+ if (vtime != null) {
+ lastViolationTime = vtime;
+ }
+ clampViolationTimeMap(mLastViolationTime, Math.max(MIN_LOG_INTERVAL_MS,
+ Math.max(MIN_DIALOG_INTERVAL_MS, MIN_DROPBOX_INTERVAL_MS)));
+ } else {
+ mLastViolationTime = new ArrayMap<>(1);
+ }
+ mLastViolationTime.put(crashFingerprint, now);
+ }
long timeSinceLastViolationMillis =
lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime);
@@ -1780,7 +1789,8 @@
penaltyMask |= PENALTY_DIALOG;
}
- if (info.penaltyEnabled(PENALTY_DROPBOX) && lastViolationTime == 0) {
+ if (info.penaltyEnabled(PENALTY_DROPBOX)
+ && timeSinceLastViolationMillis > MIN_DROPBOX_INTERVAL_MS) {
penaltyMask |= PENALTY_DROPBOX;
}
@@ -2215,6 +2225,23 @@
@UnsupportedAppUsage
private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<>();
+ /**
+ * Clamp the given map by removing elements with timestamp older than the given retainSince.
+ */
+ private static void clampViolationTimeMap(final @NonNull Map<Integer, Long> violationTime,
+ final long retainSince) {
+ final Iterator<Map.Entry<Integer, Long>> iterator = violationTime.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<Integer, Long> e = iterator.next();
+ if (e.getValue() < retainSince) {
+ // Remove stale entries
+ iterator.remove();
+ }
+ }
+ // Ideally we'd cap the total size of the map, though it'll involve quickselect of topK,
+ // seems not worth it (saving some space immediately but they will be obsoleted soon anyway)
+ }
+
/** @hide */
public static void onVmPolicyViolation(Violation originStack) {
onVmPolicyViolation(originStack, false);
@@ -2238,13 +2265,17 @@
final long now = SystemClock.uptimeMillis();
long lastViolationTime;
long timeSinceLastViolationMillis = Long.MAX_VALUE;
- synchronized (sLastVmViolationTime) {
- if (sLastVmViolationTime.containsKey(fingerprint)) {
- lastViolationTime = sLastVmViolationTime.get(fingerprint);
- timeSinceLastViolationMillis = now - lastViolationTime;
- }
- if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
- sLastVmViolationTime.put(fingerprint, now);
+ if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
+ synchronized (sLastVmViolationTime) {
+ if (sLastVmViolationTime.containsKey(fingerprint)) {
+ lastViolationTime = sLastVmViolationTime.get(fingerprint);
+ timeSinceLastViolationMillis = now - lastViolationTime;
+ }
+ if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
+ sLastVmViolationTime.put(fingerprint, now);
+ }
+ clampViolationTimeMap(sLastVmViolationTime,
+ now - Math.max(MIN_VM_INTERVAL_MS, MIN_LOG_INTERVAL_MS));
}
}
if (timeSinceLastViolationMillis <= MIN_VM_INTERVAL_MS) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index a415dc5..2465b0e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2699,15 +2699,12 @@
* @param name the user's name
* @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
* @param flags UserInfo flags that specify user properties.
- * @return the {@link UserInfo} object for the created user,
- * or throws {@link UserOperationException} if the user could not be created
- * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
- * (otherwise returns {@code null}).
+ * @return the {@link UserInfo} object for the created user, or {@code null} if the user
+ * could not be created.
*
- * @throws UserOperationException if the user could not be created and the calling app is
- * targeting {@link android.os.Build.VERSION_CODES#R} or above.
- * @hide
* @see UserInfo
+ *
+ * @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
@@ -2716,8 +2713,7 @@
try {
return mService.createUserWithThrow(name, userType, flags);
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ return null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2743,25 +2739,19 @@
* com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION}.
*
* @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
- * @return the {@link UserInfo} object for the created user,
- * or throws {@link UserOperationException} if the user could not be created
- * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
- * (otherwise returns {@code null}).
+ * @return the {@link UserInfo} object for the created user.
*
- * @throws UserOperationException if the user could not be created and the calling app is
- * targeting {@link android.os.Build.VERSION_CODES#R} or above.
- *
+ * @throws UserOperationException if the user could not be created.
* @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
- public @Nullable UserInfo preCreateUser(@NonNull String userType)
+ public @NonNull UserInfo preCreateUser(@NonNull String userType)
throws UserOperationException {
try {
return mService.preCreateUserWithThrow(userType);
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ throw UserOperationException.from(e);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2771,18 +2761,14 @@
* Creates a guest user and configures it.
* @param context an application context
* @param name the name to set for the user
- * @return the {@link UserInfo} object for the created user,
- * or throws {@link UserOperationException} if the user could not be created
- * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
- * (otherwise returns {@code null}).
+ * @return the {@link UserInfo} object for the created user, or {@code null} if the user
+ * could not be created.
*
- * @throws UserOperationException if the user could not be created and the calling app is
- * targeting {@link android.os.Build.VERSION_CODES#R} or above.
* @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
- public UserInfo createGuest(Context context, String name) throws UserOperationException {
+ public UserInfo createGuest(Context context, String name) {
UserInfo guest = null;
try {
guest = mService.createUserWithThrow(name, USER_TYPE_FULL_GUEST, 0);
@@ -2791,8 +2777,7 @@
Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
}
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ return null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2902,26 +2887,20 @@
* @param userId new user will be a profile of this user.
* @param disallowedPackages packages that will not be installed in the profile being created.
*
- * @return the {@link UserInfo} object for the created user,
- * or throws {@link UserOperationException} if the user could not be created
- * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
- * (otherwise returns {@code null}).
+ * @return the {@link UserInfo} object for the created user, or {@code null} if the user could
+ * not be created.
*
- * @throws UserOperationException if the user could not be created and the calling app is
- * targeting {@link android.os.Build.VERSION_CODES#R} or above.
* @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
public UserInfo createProfileForUser(String name, @NonNull String userType,
- @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages)
- throws UserOperationException {
+ @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages) {
try {
return mService.createProfileForUserWithThrow(name, userType, flags, userId,
disallowedPackages);
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ return null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2938,13 +2917,12 @@
Manifest.permission.CREATE_USERS})
public UserInfo createProfileForUserEvenWhenDisallowed(String name,
@NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId,
- String[] disallowedPackages) throws UserOperationException {
+ String[] disallowedPackages) {
try {
return mService.createProfileForUserEvenWhenDisallowedWithThrow(name, userType, flags,
userId, disallowedPackages);
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ return null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2955,18 +2933,14 @@
* restrictions and adds shared accounts.
*
* @param name profile's name
- * @return the {@link UserInfo} object for the created user,
- * or throws {@link UserOperationException} if the user could not be created
- * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
- * (otherwise returns {@code null}).
+ * @return the {@link UserInfo} object for the created user, or {@code null} if the user
+ * could not be created.
*
- * @throws UserOperationException if the user could not be created and the calling app is
- * targeting {@link android.os.Build.VERSION_CODES#R} or above.
* @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
- public UserInfo createRestrictedProfile(String name) throws UserOperationException {
+ public UserInfo createRestrictedProfile(String name) {
try {
UserHandle parentUserHandle = Process.myUserHandle();
UserInfo user = mService.createRestrictedProfileWithThrow(name,
@@ -2977,8 +2951,7 @@
}
return user;
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ return null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -4009,15 +3982,15 @@
* Sets the user's photo.
* @param userId the user for whom to change the photo.
* @param icon the bitmap to set as the photo.
+ *
* @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
- public void setUserIcon(@UserIdInt int userId, @NonNull Bitmap icon)
- throws UserOperationException {
+ public void setUserIcon(@UserIdInt int userId, @NonNull Bitmap icon) {
try {
mService.setUserIcon(userId, icon);
} catch (ServiceSpecificException e) {
- throw UserOperationException.from(e);
+ return;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -4027,6 +4000,10 @@
* Sets the context user's photo.
*
* @param icon the bitmap to set as the photo.
+ *
+ * @throws UserOperationException according to the function signature, but may not actually
+ * throw it in practice. Catch RuntimeException instead.
+ *
* @hide
*/
@SystemApi
diff --git a/core/java/android/os/strictmode/Violation.java b/core/java/android/os/strictmode/Violation.java
index 31c7d58..0edb78a 100644
--- a/core/java/android/os/strictmode/Violation.java
+++ b/core/java/android/os/strictmode/Violation.java
@@ -18,7 +18,58 @@
/** Root class for all StrictMode violations. */
public abstract class Violation extends Throwable {
+ private int mHashCode;
+ private boolean mHashCodeValid;
+
Violation(String message) {
super(message);
}
+
+ @Override
+ public int hashCode() {
+ synchronized (this) {
+ if (mHashCodeValid) {
+ return mHashCode;
+ }
+ final String message = getMessage();
+ final Throwable cause = getCause();
+ int hashCode = message != null ? message.hashCode() : getClass().hashCode();
+ hashCode = hashCode * 37 + calcStackTraceHashCode(getStackTrace());
+ hashCode = hashCode * 37 + (cause != null ? cause.toString().hashCode() : 0);
+ mHashCodeValid = true;
+ return mHashCode = hashCode;
+ }
+ }
+
+ @Override
+ public synchronized Throwable initCause(Throwable cause) {
+ mHashCodeValid = false;
+ return super.initCause(cause);
+ }
+
+ @Override
+ public void setStackTrace(StackTraceElement[] stackTrace) {
+ super.setStackTrace(stackTrace);
+ synchronized (this) {
+ mHashCodeValid = false;
+ }
+ }
+
+ @Override
+ public synchronized Throwable fillInStackTrace() {
+ mHashCodeValid = false;
+ return super.fillInStackTrace();
+ }
+
+ private static int calcStackTraceHashCode(final StackTraceElement[] stackTrace) {
+ int hashCode = 17;
+ if (stackTrace != null) {
+ for (int i = 0; i < stackTrace.length; i++) {
+ if (stackTrace[i] != null) {
+ hashCode = hashCode * 37 + stackTrace[i].hashCode();
+ }
+ }
+ }
+ return hashCode;
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4abb2c5..64d9c9d 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12009,7 +12009,7 @@
* @see #ENABLE_RESTRICTED_BUCKET
* @hide
*/
- public static final int DEFAULT_ENABLE_RESTRICTED_BUCKET = 1;
+ public static final int DEFAULT_ENABLE_RESTRICTED_BUCKET = 0;
/**
* Whether or not app auto restriction is enabled. When it is enabled, settings app will
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ed40483..b860bac 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -21,7 +21,6 @@
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.LAST_TYPE;
import static android.view.InsetsState.SIZE;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -4617,6 +4616,9 @@
}
void dispatchDetachedFromWindow() {
+ // Make sure we free-up insets resources if view never received onWindowFocusLost()
+ // because of a die-signal
+ mInsetsController.onWindowFocusLost();
mFirstInputStage.onDetachedFromWindow();
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
diff --git a/core/proto/android/stats/connectivity/tethering.proto b/core/proto/android/stats/connectivity/tethering.proto
index 6303b7d..13f0b8c 100644
--- a/core/proto/android/stats/connectivity/tethering.proto
+++ b/core/proto/android/stats/connectivity/tethering.proto
@@ -87,7 +87,7 @@
enum UserType {
// Unknown.
- USER_UNKOWNN = 0;
+ USER_UNKNOWN = 0;
// Settings.
USER_SETTINGS = 1;
// System UI.
diff --git a/core/res/res/drawable/chooser_action_button_bg.xml b/core/res/res/drawable/chooser_action_button_bg.xml
index a434c0b..0dd9e9c7 100644
--- a/core/res/res/drawable/chooser_action_button_bg.xml
+++ b/core/res/res/drawable/chooser_action_button_bg.xml
@@ -25,8 +25,8 @@
<shape android:shape="rectangle">
<corners android:radius="16dp"></corners>
<stroke android:width="1dp"
- android:color="?attr/textColorSecondary" />
- <solid android:color="?attr/colorBackground" />
+ android:color="?attr/opacityListDivider" />
+ <solid android:color="?attr/colorBackgroundFloating" />
</shape>
</inset>
</item>
diff --git a/core/res/res/layout/chooser_action_button.xml b/core/res/res/layout/chooser_action_button.xml
index 119b2e9..6af7937 100644
--- a/core/res/res/layout/chooser_action_button.xml
+++ b/core/res/res/layout/chooser_action_button.xml
@@ -19,12 +19,12 @@
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:drawablePadding="8dp"
- android:textColor="?android:textColorSecondary"
+ android:textColor="?android:textColorPrimary"
android:textSize="12sp"
android:maxWidth="192dp"
android:singleLine="true"
android:clickable="true"
android:background="@drawable/chooser_action_button_bg"
- android:drawableTint="?android:attr/colorControlNormal"
+ android:drawableTint="@color/chooser_chip_icon"
android:drawableTintMode="src_in"
/>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index c413f8b..1242c6d 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -230,6 +230,7 @@
<color name="resolver_text_color_secondary_dark">#ffC4C6C6</color>
<color name="resolver_empty_state_text">#FF202124</color>
<color name="resolver_empty_state_icon">#FF5F6368</color>
+ <color name="chooser_chip_icon">#FF1A73E8</color> <!-- Blue 600 -->
<!-- Color for personal app suspension notification button text and icon tint. -->
<color name="personal_apps_suspension_notification_color">#1A73E8</color>
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index eb6ee95..5f231ff 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -157,7 +157,7 @@
table_value->dataType = entry->type;
table_value->data = entry->value;
- return Result(ResTable_entry_handle::managed(table_entry));
+ return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); }));
}
static bool is_word_aligned(const void* data) {
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 21be81c..e351a46 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1601,8 +1601,8 @@
entry_ = handle.entry_;
}
- inline static ResTable_entry_handle managed(ResTable_entry* entry) {
- return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry));
+ inline static ResTable_entry_handle managed(ResTable_entry* entry, void (*deleter)(void *)) {
+ return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, deleter));
}
inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) {
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 2cca669..00a4c7e 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -448,8 +448,7 @@
* Returns an array of supported encapsulation modes for the device.
*
* The array can include any of the {@code AudioTrack} encapsulation modes,
- * e.g. {@link AudioTrack#ENCAPSULATION_MODE_NONE},
- * or {@link AudioTrack#ENCAPSULATION_MODE_ELEMENTARY_STREAM}.
+ * e.g. {@link AudioTrack#ENCAPSULATION_MODE_ELEMENTARY_STREAM}.
*
* @return An array of supported encapsulation modes for the device. This
* may be an empty array if no encapsulation modes are supported.
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 9f3fc5d..1c0a526 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -280,7 +280,7 @@
/**
* Encapsulation metadata type for framework tuner information.
*
- * TODO(b/147778408) Link: Fill in Tuner API info.
+ * Refer to the Android Media TV Tuner API for details.
*/
public static final int ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER = 1;
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index bcff634..9d52098 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -30,7 +30,7 @@
*/
@ProvidesInterface(version = FalsingManager.VERSION)
public interface FalsingManager {
- int VERSION = 3;
+ int VERSION = 4;
void onSuccessfulUnlock();
@@ -88,11 +88,11 @@
void onScreenOff();
- void onNotificatonStopDismissing();
+ void onNotificationStopDismissing();
void onNotificationDismissed();
- void onNotificatonStartDismissing();
+ void onNotificationStartDismissing();
void onNotificationDoubleTap(boolean accepted, float dx, float dy);
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml b/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml
new file mode 100644
index 0000000..3790378
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/media_seamless_border">
+ <item android:id="@android:id/background">
+ <shape
+ android:color="@android:color/transparent">
+ <stroke android:width="1dp" android:color="@color/media_seamless_border"/>
+ <corners android:radius="24dp"/>
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/qs_media_divider.xml b/packages/SystemUI/res-keyguard/layout/qs_media_divider.xml
new file mode 100644
index 0000000..1be489c
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/qs_media_divider.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginBottom="16dp"
+ android:background="@color/media_divider">
+</View>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
index dc91731..ee1173b 100644
--- a/packages/SystemUI/res/layout/media_carousel.xml
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -23,7 +23,7 @@
android:clipChildren="false"
android:clipToPadding="false"
>
- <com.android.systemui.media.UnboundHorizontalScrollView
+ <com.android.systemui.media.MediaScrollView
android:id="@+id/media_carousel_scroller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -41,14 +41,12 @@
>
<!-- QSMediaPlayers will be added here dynamically -->
</LinearLayout>
- </com.android.systemui.media.UnboundHorizontalScrollView>
+ </com.android.systemui.media.MediaScrollView>
<com.android.systemui.qs.PageIndicator
android:id="@+id/media_page_indicator"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginBottom="4dp"
- android:layout_gravity="center_horizontal|bottom"
- android:gravity="center"
android:tint="@color/media_primary_text"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_carousel_settings_button.xml b/packages/SystemUI/res/layout/media_carousel_settings_button.xml
new file mode 100644
index 0000000..4570cb1
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_carousel_settings_button.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/settings_cog"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/controls_media_settings_button"
+ android:paddingStart="30dp"
+ android:paddingEnd="30dp"
+ android:paddingBottom="20dp"
+ android:paddingTop="20dp"
+ android:src="@drawable/ic_settings"
+ android:tint="@color/notification_gear_color"
+ android:visibility="invisible"
+ android:forceHasOverlappingRendering="false"/>
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index cc7ed19..5b86492 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -94,7 +94,7 @@
android:id="@+id/media_seamless"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:foreground="@*android:drawable/media_seamless_background"
+ android:foreground="@drawable/qs_media_seamless_background"
android:background="@drawable/qs_media_light_source"
android:orientation="horizontal"
android:forceHasOverlappingRendering="false"
@@ -115,7 +115,7 @@
android:id="@+id/media_seamless_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:text="@*android:string/ext_media_seamless_action"
android:textColor="@color/media_primary_text"
@@ -161,6 +161,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:singleLine="true"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
android:textSize="14sp" />
<!-- Song name -->
@@ -178,7 +179,7 @@
android:id="@+id/header_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:textColor="@color/media_secondary_text"
android:textSize="14sp" />
@@ -186,8 +187,8 @@
<com.android.internal.widget.CachingIconView
android:id="@+id/icon"
android:tint="@color/media_primary_text"
- android:layout_width="16dp"
- android:layout_height="16dp" />
+ android:layout_width="20dp"
+ android:layout_height="20dp" />
<!-- Buttons to remove this view when no longer needed -->
<include
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 761ab03..597644b 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -62,6 +62,8 @@
android:focusable="true"
android:accessibilityTraversalBefore="@android:id/edit">
<include layout="@layout/qs_footer_impl" />
+ <include layout="@layout/qs_media_divider"
+ android:id="@+id/divider"/>
</com.android.systemui.qs.QSPanel>
</com.android.systemui.qs.NonInterceptingScrollView>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 196357c..cb9e178 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -81,6 +81,8 @@
<color name="global_screenshot_dismiss_foreground">#FFFFFF</color>
<color name="global_screenshot_background_protection_start">#80000000</color> <!-- 50% black -->
+ <!-- Media -->
+ <color name="media_divider">#85ffffff</color>
<!-- Biometric dialog colors -->
<color name="biometric_dialog_gray">#ff888888</color>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d8a3c8c..994a181 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -248,6 +248,8 @@
<color name="media_secondary_text">#99ffffff</color> <!-- 60% -->
<color name="media_seekbar_progress">#c0ffffff</color>
<color name="media_disabled">#80ffffff</color>
+ <color name="media_seamless_border">#26ffffff</color> <!-- 15% -->
+ <color name="media_divider">#1d000000</color>
<!-- controls -->
<color name="control_primary_text">#E6FFFFFF</color>
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index c985b08..50828e8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -1504,6 +1504,7 @@
&& ((BadgedImageView) v).getKey().equals(bubble.getKey())) {
mBubbleContainer.removeViewAt(i);
bubble.cleanupViews();
+ updatePointerPosition();
logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
return;
}
@@ -1554,6 +1555,14 @@
mBubbleData.setShowingOverflow(true);
}
+ if (mIsExpanded && mIsExpansionAnimating) {
+ // If the bubble selection changed during the expansion animation, the expanding bubble
+ // probably crashed or immediately removed itself (or, we just got unlucky with a new
+ // auto-expanding bubble showing up at just the right time). Cancel the animations so we
+ // can start fresh.
+ cancelAllExpandCollapseSwitchAnimations();
+ }
+
// If we're expanded, screenshot the currently expanded bubble (before expanding the newly
// selected bubble) so we can animate it out.
if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
@@ -1879,35 +1888,37 @@
mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
}
- mDelayedAnimationHandler.postDelayed(() ->
- PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
- .spring(AnimatableScaleMatrix.SCALE_X,
- AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
- mScaleInSpringConfig)
- .spring(AnimatableScaleMatrix.SCALE_Y,
- AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
- mScaleInSpringConfig)
- .addUpdateListener((target, values) -> {
- if (mExpandedBubble.getIconView() == null) {
- return;
- }
- mExpandedViewContainerMatrix.postTranslate(
- mExpandedBubble.getIconView().getTranslationX()
- - bubbleWillBeAtX,
- 0);
- mExpandedViewContainer.setAnimationMatrix(
- mExpandedViewContainerMatrix);
- })
- .withEndActions(() -> {
- if (mExpandedBubble != null
- && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView()
- .setContentVisibility(true);
- mExpandedBubble.getExpandedView()
- .setSurfaceZOrderedOnTop(false);
- }
- })
- .start(), startDelay);
+ mDelayedAnimationHandler.postDelayed(() -> {
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+ .spring(AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+ mScaleInSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_Y,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+ mScaleInSpringConfig)
+ .addUpdateListener((target, values) -> {
+ if (mExpandedBubble.getIconView() == null) {
+ return;
+ }
+ mExpandedViewContainerMatrix.postTranslate(
+ mExpandedBubble.getIconView().getTranslationX()
+ - bubbleWillBeAtX,
+ 0);
+ mExpandedViewContainer.setAnimationMatrix(
+ mExpandedViewContainerMatrix);
+ })
+ .withEndActions(() -> {
+ if (mExpandedBubble != null
+ && mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView()
+ .setContentVisibility(true);
+ mExpandedBubble.getExpandedView()
+ .setSurfaceZOrderedOnTop(false);
+ }
+ })
+ .start();
+ }, startDelay);
}
private void animateCollapse() {
@@ -2035,6 +2046,7 @@
return;
}
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
.spring(AnimatableScaleMatrix.SCALE_X,
AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
@@ -2068,6 +2080,15 @@
mIsBubbleSwitchAnimating = false;
}
+ private void cancelAllExpandCollapseSwitchAnimations() {
+ cancelDelayedExpandCollapseSwitchAnimations();
+
+ PhysicsAnimator.getInstance(mAnimatingOutSurfaceView).cancel();
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
+
+ mExpandedViewContainer.setAnimationMatrix(null);
+ }
+
private void notifyExpansionChanged(BubbleViewProvider bubble, boolean expanded) {
if (mExpandListener != null && bubble != null) {
mExpandListener.onBubbleExpandChanged(expanded, bubble.getKey());
@@ -2587,6 +2608,7 @@
bev.setContentVisibility(false);
mExpandedViewContainerMatrix.setScaleX(0f);
mExpandedViewContainerMatrix.setScaleY(0f);
+ mExpandedViewContainerMatrix.setTranslate(0f, 0f);
mExpandedViewContainer.setVisibility(View.INVISIBLE);
mExpandedViewContainer.setAlpha(0f);
mExpandedViewContainer.addView(bev);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 8e23252..f2a4f15 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -237,6 +237,10 @@
}
mAfterExpand = null;
+
+ // Update bubble positions in case any bubbles were added or removed during the
+ // expansion animation.
+ updateBubblePositions();
};
} else {
after = () -> {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index e105795a..646e620 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -201,7 +201,7 @@
}
@Override
- public void onNotificatonStopDismissing() {
+ public void onNotificationStopDismissing() {
}
@@ -211,7 +211,7 @@
}
@Override
- public void onNotificatonStartDismissing() {
+ public void onNotificationStartDismissing() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index 37c7a2e..cc64fb5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -481,15 +481,15 @@
mDataCollector.onNotificationDismissed();
}
- public void onNotificatonStartDismissing() {
+ public void onNotificationStartDismissing() {
if (FalsingLog.ENABLED) {
- FalsingLog.i("onNotificatonStartDismissing", "");
+ FalsingLog.i("onNotificationStartDismissing", "");
}
mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
mDataCollector.onNotificatonStartDismissing();
}
- public void onNotificatonStopDismissing() {
+ public void onNotificationStopDismissing() {
mDataCollector.onNotificatonStopDismissing();
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 79b691b..ef2ef45 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -302,8 +302,8 @@
}
@Override
- public void onNotificatonStopDismissing() {
- mInternalFalsingManager.onNotificatonStopDismissing();
+ public void onNotificationStopDismissing() {
+ mInternalFalsingManager.onNotificationStopDismissing();
}
@Override
@@ -312,8 +312,8 @@
}
@Override
- public void onNotificatonStartDismissing() {
- mInternalFalsingManager.onNotificatonStartDismissing();
+ public void onNotificationStartDismissing() {
+ mInternalFalsingManager.onNotificationStartDismissing();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index caab187..62254a6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -380,7 +380,7 @@
@Override
- public void onNotificatonStopDismissing() {
+ public void onNotificationStopDismissing() {
}
@Override
@@ -388,7 +388,7 @@
}
@Override
- public void onNotificatonStartDismissing() {
+ public void onNotificationStartDismissing() {
updateInteractionType(Classifier.NOTIFICATION_DISMISS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index a6af6a1..ec8bfc6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -73,7 +73,7 @@
companion object {
private const val BIND_RETRY_DELAY = 1000L // ms
- private const val LOAD_TIMEOUT_SECONDS = 30L // seconds
+ private const val LOAD_TIMEOUT_SECONDS = 20L // seconds
private const val MAX_BIND_RETRIES = 5
private const val DEBUG = true
private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 4884781..b282157 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -174,30 +174,32 @@
subtitleView.visibility = View.GONE
} else {
statusText.visibility = View.GONE
- }
- pageIndicator.setNumPages(listOfStructures.size)
- pageIndicator.setLocation(0f)
- pageIndicator.visibility =
- if (listOfStructures.size > 1) View.VISIBLE else View.INVISIBLE
- ControlsAnimations.enterAnimation(pageIndicator).apply {
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- // Position the tooltip if necessary after animations are complete
- // so we can get the position on screen. The tooltip is not
- // rooted in the layout root.
- if (pageIndicator.visibility == View.VISIBLE &&
+ pageIndicator.setNumPages(listOfStructures.size)
+ pageIndicator.setLocation(0f)
+ pageIndicator.visibility =
+ if (listOfStructures.size > 1) View.VISIBLE else View.INVISIBLE
+
+ ControlsAnimations.enterAnimation(pageIndicator).apply {
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ // Position the tooltip if necessary after animations are complete
+ // so we can get the position on screen. The tooltip is not
+ // rooted in the layout root.
+ if (pageIndicator.visibility == View.VISIBLE &&
mTooltipManager != null) {
- val p = IntArray(2)
- pageIndicator.getLocationOnScreen(p)
- val x = p[0] + pageIndicator.width / 2
- val y = p[1] + pageIndicator.height
- mTooltipManager?.show(R.string.controls_structure_tooltip, x, y)
+ val p = IntArray(2)
+ pageIndicator.getLocationOnScreen(p)
+ val x = p[0] + pageIndicator.width / 2
+ val y = p[1] + pageIndicator.height
+ mTooltipManager?.show(
+ R.string.controls_structure_tooltip, x, y)
+ }
}
- }
- })
- }.start()
- ControlsAnimations.enterAnimation(structurePager).start()
+ })
+ }.start()
+ ControlsAnimations.enterAnimation(structurePager).start()
+ }
}
}, Consumer { runnable -> cancelLoadRunnable = runnable })
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 5f43e43..8ef20f8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -46,7 +46,9 @@
})
}
- private var view: MediaHeaderView? = null
+ var visibilityChangedListener: ((Boolean) -> Unit)? = null
+ var view: MediaHeaderView? = null
+ private set
/**
* Attach this controller to a media view, initializing its state
@@ -54,9 +56,10 @@
fun attach(mediaView: MediaHeaderView) {
view = mediaView
// First let's set the desired state that we want for this host
- mediaHost.visibleChangedListener = { updateVisibility() }
+ mediaHost.addVisibilityChangeListener { updateVisibility() }
mediaHost.expansion = 0.0f
mediaHost.showsOnlyActiveMedia = true
+ mediaHost.falsingProtectionNeeded = true
// Let's now initialize this view, which also creates the host view for us.
mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN)
@@ -70,6 +73,11 @@
!bypassController.bypassEnabled &&
keyguardOrUserSwitcher &&
notifLockscreenUserManager.shouldShowLockscreenNotifications()
- view?.visibility = if (shouldBeVisible) View.VISIBLE else View.GONE
+ val previousVisibility = view?.visibility ?: View.GONE
+ val newVisibility = if (shouldBeVisible) View.VISIBLE else View.GONE
+ view?.visibility = newVisibility
+ if (previousVisibility != newVisibility) {
+ visibilityChangedListener?.invoke(shouldBeVisible)
+ }
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
similarity index 65%
rename from packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
rename to packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index bccc3ab..db45a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -1,42 +1,66 @@
package com.android.systemui.media
import android.content.Context
+import android.content.Intent
import android.graphics.Color
+import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
import android.view.LayoutInflater
-import android.view.GestureDetector
-import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
-import android.widget.HorizontalScrollView
import android.widget.LinearLayout
-import androidx.core.view.GestureDetectorCompat
import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
import com.android.systemui.statusbar.notification.VisualStabilityManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.animation.requiresRemeasuring
+import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton
-private const val FLING_SLOP = 1000000
+private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
/**
* Class that is responsible for keeping the view carousel up to date.
* This also handles changes in state and applies them to the media carousel like the expansion.
*/
@Singleton
-class MediaViewManager @Inject constructor(
+class MediaCarouselController @Inject constructor(
private val context: Context,
private val mediaControlPanelFactory: Provider<MediaControlPanel>,
private val visualStabilityManager: VisualStabilityManager,
private val mediaHostStatesManager: MediaHostStatesManager,
+ private val activityStarter: ActivityStarter,
+ @Main executor: DelayableExecutor,
mediaManager: MediaDataCombineLatest,
- configurationController: ConfigurationController
+ configurationController: ConfigurationController,
+ mediaDataManager: MediaDataManager,
+ falsingManager: FalsingManager
) {
+ /**
+ * The current width of the carousel
+ */
+ private var currentCarouselWidth: Int = 0
/**
+ * The current height of the carousel
+ */
+ private var currentCarouselHeight: Int = 0
+
+ /**
+ * Are we currently showing only active players
+ */
+ private var currentlyShowingOnlyActive: Boolean = false
+
+ /**
+ * Is the player currently visible (at the end of the transformation
+ */
+ private var playersVisible: Boolean = false
+ /**
* The desired location where we'll be at the end of the transformation. Usually this matches
* the end location, except when we're still waiting on a state update call.
*/
@@ -73,17 +97,16 @@
private var carouselMeasureHeight: Int = 0
private var playerWidthPlusPadding: Int = 0
private var desiredHostState: MediaHostState? = null
- private val mediaCarousel: HorizontalScrollView
+ private val mediaCarousel: MediaScrollView
+ private val mediaCarouselScrollHandler: MediaCarouselScrollHandler
val mediaFrame: ViewGroup
val mediaPlayers: MutableMap<String, MediaControlPanel> = mutableMapOf()
+ private lateinit var settingsButton: View
private val mediaData: MutableMap<String, MediaData> = mutableMapOf()
private val mediaContent: ViewGroup
private val pageIndicator: PageIndicator
- private val gestureDetector: GestureDetectorCompat
private val visualStabilityCallback: VisualStabilityManager.Callback
- private var activeMediaIndex: Int = 0
private var needsReordering: Boolean = false
- private var scrollIntoCurrentMedia: Int = 0
private var currentlyExpanded = true
set(value) {
if (field != value) {
@@ -93,50 +116,25 @@
}
}
}
- private val scrollChangedListener = object : View.OnScrollChangeListener {
- override fun onScrollChange(
- v: View?,
- scrollX: Int,
- scrollY: Int,
- oldScrollX: Int,
- oldScrollY: Int
- ) {
- if (playerWidthPlusPadding == 0) {
- return
- }
- onMediaScrollingChanged(scrollX / playerWidthPlusPadding,
- scrollX % playerWidthPlusPadding)
- }
- }
- private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
- override fun onFling(
- eStart: MotionEvent?,
- eCurrent: MotionEvent?,
- vX: Float,
- vY: Float
- ): Boolean {
- return this@MediaViewManager.onFling(eStart, eCurrent, vX, vY)
- }
- }
- private val touchListener = object : View.OnTouchListener {
- override fun onTouch(view: View, motionEvent: MotionEvent?): Boolean {
- return this@MediaViewManager.onTouch(view, motionEvent)
- }
- }
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
recreatePlayers()
+ inflateSettingsButton()
+ }
+
+ override fun onOverlayChanged() {
+ inflateSettingsButton()
}
}
init {
- gestureDetector = GestureDetectorCompat(context, gestureListener)
mediaFrame = inflateMediaCarousel()
mediaCarousel = mediaFrame.requireViewById(R.id.media_carousel_scroller)
pageIndicator = mediaFrame.requireViewById(R.id.media_page_indicator)
- mediaCarousel.setOnScrollChangeListener(scrollChangedListener)
- mediaCarousel.setOnTouchListener(touchListener)
- mediaCarousel.setOverScrollMode(View.OVER_SCROLL_NEVER)
+ mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator,
+ executor, mediaDataManager::onSwipeToDismiss, this::updatePageIndicatorLocation,
+ falsingManager)
+ inflateSettingsButton()
mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
configurationController.addCallback(configListener)
visualStabilityCallback = VisualStabilityManager.Callback {
@@ -161,6 +159,11 @@
removePlayer(key)
}
})
+ mediaFrame.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
+ // The pageIndicator is not laid out yet when we get the current state update,
+ // Lets make sure we have the right dimensions
+ updatePageIndicatorLocation()
+ }
mediaHostStatesManager.addCallback(object : MediaHostStatesManager.Callback {
override fun onHostStateChanged(location: Int, mediaHostState: MediaHostState) {
if (location == desiredLocation) {
@@ -170,6 +173,20 @@
})
}
+ private fun inflateSettingsButton() {
+ val settings = LayoutInflater.from(context).inflate(R.layout.media_carousel_settings_button,
+ mediaFrame, false) as View
+ if (this::settingsButton.isInitialized) {
+ mediaFrame.removeView(settingsButton)
+ }
+ settingsButton = settings
+ mediaFrame.addView(settingsButton)
+ mediaCarouselScrollHandler.onSettingsButtonUpdated(settings)
+ settingsButton.setOnClickListener {
+ activityStarter.startActivity(settingsIntent, true /* dismissShade */)
+ }
+ }
+
private fun inflateMediaCarousel(): ViewGroup {
return LayoutInflater.from(context).inflate(R.layout.media_carousel,
UniqueObjectHostView(context), false) as ViewGroup
@@ -183,68 +200,7 @@
mediaContent.addView(view, 0)
}
}
- updateMediaPaddings()
- updatePlayerVisibilities()
- }
-
- private fun onMediaScrollingChanged(newIndex: Int, scrollInAmount: Int) {
- val wasScrolledIn = scrollIntoCurrentMedia != 0
- scrollIntoCurrentMedia = scrollInAmount
- val nowScrolledIn = scrollIntoCurrentMedia != 0
- if (newIndex != activeMediaIndex || wasScrolledIn != nowScrolledIn) {
- activeMediaIndex = newIndex
- updatePlayerVisibilities()
- }
- val location = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
- scrollInAmount.toFloat() / playerWidthPlusPadding else 0f
- pageIndicator.setLocation(location)
- }
-
- private fun onTouch(view: View, motionEvent: MotionEvent?): Boolean {
- if (gestureDetector.onTouchEvent(motionEvent)) {
- return true
- }
- if (motionEvent?.getAction() == MotionEvent.ACTION_UP) {
- val pos = mediaCarousel.scrollX % playerWidthPlusPadding
- if (pos > playerWidthPlusPadding / 2) {
- mediaCarousel.smoothScrollBy(playerWidthPlusPadding - pos, 0)
- } else {
- mediaCarousel.smoothScrollBy(-1 * pos, 0)
- }
- return true
- }
- return view.onTouchEvent(motionEvent)
- }
-
- private fun onFling(
- eStart: MotionEvent?,
- eCurrent: MotionEvent?,
- vX: Float,
- vY: Float
- ): Boolean {
- if (vX * vX < 0.5 * vY * vY) {
- return false
- }
- if (vX * vX < FLING_SLOP) {
- return false
- }
- val pos = mediaCarousel.scrollX
- val currentIndex = if (playerWidthPlusPadding > 0) pos / playerWidthPlusPadding else 0
- var destIndex = if (vX <= 0) currentIndex + 1 else currentIndex
- destIndex = Math.max(0, destIndex)
- destIndex = Math.min(mediaContent.getChildCount() - 1, destIndex)
- val view = mediaContent.getChildAt(destIndex)
- mediaCarousel.smoothScrollTo(view.left, mediaCarousel.scrollY)
- return true
- }
-
- private fun updatePlayerVisibilities() {
- val scrolledIn = scrollIntoCurrentMedia != 0
- for (i in 0 until mediaContent.childCount) {
- val view = mediaContent.getChildAt(i)
- val visible = (i == activeMediaIndex) || ((i == (activeMediaIndex + 1)) && scrolledIn)
- view.visibility = if (visible) View.VISIBLE else View.INVISIBLE
- }
+ mediaCarouselScrollHandler.onPlayersChanged()
}
private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) {
@@ -259,6 +215,7 @@
existingPlayer = mediaControlPanelFactory.get()
existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context),
mediaContent))
+ existingPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
mediaPlayers[key] = existingPlayer
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
@@ -280,28 +237,18 @@
}
}
existingPlayer?.bind(data)
- updateMediaPaddings()
updatePageIndicator()
- updatePlayerVisibilities()
+ mediaCarouselScrollHandler.onPlayersChanged()
mediaCarousel.requiresRemeasuring = true
}
private fun removePlayer(key: String) {
val removed = mediaPlayers.remove(key)
removed?.apply {
- val beforeActive = mediaContent.indexOfChild(removed.view?.player) <=
- activeMediaIndex
+ mediaCarouselScrollHandler.onPrePlayerRemoved(removed)
mediaContent.removeView(removed.view?.player)
removed.onDestroy()
- updateMediaPaddings()
- if (beforeActive) {
- // also update the index here since the scroll below might not always lead
- // to a scrolling changed
- activeMediaIndex = Math.max(0, activeMediaIndex - 1)
- mediaCarousel.scrollX = Math.max(mediaCarousel.scrollX -
- playerWidthPlusPadding, 0)
- }
- updatePlayerVisibilities()
+ mediaCarouselScrollHandler.onPlayersChanged()
updatePageIndicator()
}
}
@@ -317,20 +264,6 @@
}
}
- private fun updateMediaPaddings() {
- val padding = context.resources.getDimensionPixelSize(R.dimen.qs_media_padding)
- val childCount = mediaContent.childCount
- for (i in 0 until childCount) {
- val mediaView = mediaContent.getChildAt(i)
- val desiredPaddingEnd = if (i == childCount - 1) 0 else padding
- val layoutParams = mediaView.layoutParams as ViewGroup.MarginLayoutParams
- if (layoutParams.marginEnd != desiredPaddingEnd) {
- layoutParams.marginEnd = desiredPaddingEnd
- mediaView.layoutParams = layoutParams
- }
- }
- }
-
private fun updatePageIndicator() {
val numPages = mediaContent.getChildCount()
pageIndicator.setNumPages(numPages, Color.WHITE)
@@ -342,6 +275,12 @@
/**
* Set a new interpolated state for all players. This is a state that is usually controlled
* by a finger movement where the user drags from one state to the next.
+ *
+ * @param startLocation the start location of our state or -1 if this is directly set
+ * @param endLocation the ending location of our state.
+ * @param progress the progress of the transition between startLocation and endlocation. If
+ * this is not a guided transformation, this will be 1.0f
+ * @param immediately should this state be applied immediately, canceling all animations?
*/
fun setCurrentState(
@MediaLocation startLocation: Int,
@@ -349,9 +288,6 @@
progress: Float,
immediately: Boolean
) {
- // Hack: Since the indicator doesn't move with the player expansion, just make it disappear
- // and then reappear at the end.
- pageIndicator.alpha = if (progress == 1f || progress == 0f) 1f else 0f
if (startLocation != currentStartLocation ||
endLocation != currentEndLocation ||
progress != currentTransitionProgress ||
@@ -363,6 +299,51 @@
for (mediaPlayer in mediaPlayers.values) {
updatePlayerToState(mediaPlayer, immediately)
}
+ maybeResetSettingsCog()
+ }
+ }
+
+ private fun updatePageIndicatorLocation() {
+ // Update the location of the page indicator, carousel clipping
+ pageIndicator.translationX = (currentCarouselWidth - pageIndicator.width) / 2.0f +
+ mediaCarouselScrollHandler.contentTranslation
+ val layoutParams = pageIndicator.layoutParams as ViewGroup.MarginLayoutParams
+ pageIndicator.translationY = (currentCarouselHeight - pageIndicator.height -
+ layoutParams.bottomMargin).toFloat()
+ }
+
+ /**
+ * Update the dimension of this carousel.
+ */
+ private fun updateCarouselDimensions() {
+ var width = 0
+ var height = 0
+ for (mediaPlayer in mediaPlayers.values) {
+ val controller = mediaPlayer.mediaViewController
+ width = Math.max(width, controller.currentWidth)
+ height = Math.max(height, controller.currentHeight)
+ }
+ if (width != currentCarouselWidth || height != currentCarouselHeight) {
+ currentCarouselWidth = width
+ currentCarouselHeight = height
+ mediaCarouselScrollHandler.setCarouselBounds(currentCarouselWidth, currentCarouselHeight)
+ updatePageIndicatorLocation()
+ }
+ }
+
+ private fun maybeResetSettingsCog() {
+ val hostStates = mediaHostStatesManager.mediaHostStates
+ val endShowsActive = hostStates[currentEndLocation]?.showsOnlyActiveMedia
+ ?: true
+ val startShowsActive = hostStates[currentStartLocation]?.showsOnlyActiveMedia
+ ?: endShowsActive
+ if (currentlyShowingOnlyActive != endShowsActive ||
+ ((currentTransitionProgress != 1.0f && currentTransitionProgress != 0.0f) &&
+ startShowsActive != endShowsActive)) {
+ /// Whenever we're transitioning from between differing states or the endstate differs
+ // we reset the translation
+ currentlyShowingOnlyActive = endShowsActive
+ mediaCarouselScrollHandler.resetTranslation(animate = true)
}
}
@@ -404,6 +385,15 @@
}
mediaPlayer.mediaViewController.onLocationPreChange(desiredLocation)
}
+ mediaCarouselScrollHandler.showsSettingsButton = !it.showsOnlyActiveMedia
+ mediaCarouselScrollHandler.falsingProtectionNeeded = it.falsingProtectionNeeded
+ val nowVisible = it.visible
+ if (nowVisible != playersVisible) {
+ playersVisible = nowVisible
+ if (nowVisible) {
+ mediaCarouselScrollHandler.resetTranslation()
+ }
+ }
updateCarouselSize()
}
}
@@ -420,16 +410,7 @@
carouselMeasureHeight = height
playerWidthPlusPadding = carouselMeasureWidth + context.resources.getDimensionPixelSize(
R.dimen.qs_media_padding)
- // The player width has changed, let's update the scroll position to make sure
- // it's still at the same place
- var newScroll = activeMediaIndex * playerWidthPlusPadding
- if (scrollIntoCurrentMedia > playerWidthPlusPadding) {
- newScroll += playerWidthPlusPadding -
- (scrollIntoCurrentMedia - playerWidthPlusPadding)
- } else {
- newScroll += scrollIntoCurrentMedia
- }
- mediaCarousel.scrollX = newScroll
+ mediaCarouselScrollHandler.playerWidthPlusPadding = playerWidthPlusPadding
// Let's remeasure the carousel
val widthSpec = desiredHostState?.measurementInput?.widthMeasureSpec ?: 0
val heightSpec = desiredHostState?.measurementInput?.heightMeasureSpec ?: 0
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
new file mode 100644
index 0000000..993c05f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -0,0 +1,516 @@
+/*
+ * 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.media
+
+import android.graphics.Outline
+import android.util.MathUtils
+import android.view.GestureDetector
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewOutlineProvider
+import androidx.core.view.GestureDetectorCompat
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.settingslib.Utils
+import com.android.systemui.Gefingerpoken
+import com.android.systemui.qs.PageIndicator
+import com.android.systemui.R
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.util.animation.PhysicsAnimator
+import com.android.systemui.util.concurrency.DelayableExecutor
+
+private const val FLING_SLOP = 1000000
+private const val DISMISS_DELAY = 100L
+private const val RUBBERBAND_FACTOR = 0.2f
+private const val SETTINGS_BUTTON_TRANSLATION_FRACTION = 0.3f
+
+/**
+ * Default spring configuration to use for animations where stiffness and/or damping ratio
+ * were not provided, and a default spring was not set via [PhysicsAnimator.setDefaultSpringConfig].
+ */
+private val translationConfig = PhysicsAnimator.SpringConfig(
+ SpringForce.STIFFNESS_MEDIUM,
+ SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+
+/**
+ * A controller class for the media scrollview, responsible for touch handling
+ */
+class MediaCarouselScrollHandler(
+ private val scrollView: MediaScrollView,
+ private val pageIndicator: PageIndicator,
+ private val mainExecutor: DelayableExecutor,
+ private val dismissCallback: () -> Unit,
+ private var translationChangedListener: () -> Unit,
+ private val falsingManager: FalsingManager
+) {
+ /**
+ * Do we need falsing protection?
+ */
+ var falsingProtectionNeeded: Boolean = false
+ /**
+ * The width of the carousel
+ */
+ private var carouselWidth: Int = 0
+
+ /**
+ * The height of the carousel
+ */
+ private var carouselHeight: Int = 0
+
+ /**
+ * How much are we scrolled into the current media?
+ */
+ private var cornerRadius: Int = 0
+
+ /**
+ * The content where the players are added
+ */
+ private var mediaContent: ViewGroup
+ /**
+ * The gesture detector to detect touch gestures
+ */
+ private val gestureDetector: GestureDetectorCompat
+
+ /**
+ * The settings button view
+ */
+ private lateinit var settingsButton: View
+
+ /**
+ * What's the currently active player index?
+ */
+ var activeMediaIndex: Int = 0
+ private set
+ /**
+ * How much are we scrolled into the current media?
+ */
+ private var scrollIntoCurrentMedia: Int = 0
+
+ /**
+ * how much is the content translated in X
+ */
+ var contentTranslation = 0.0f
+ private set(value) {
+ field = value
+ mediaContent.translationX = value
+ updateSettingsPresentation()
+ translationChangedListener.invoke()
+ updateClipToOutline()
+ }
+
+ /**
+ * The width of a player including padding
+ */
+ var playerWidthPlusPadding: Int = 0
+ set(value) {
+ field = value
+ // The player width has changed, let's update the scroll position to make sure
+ // it's still at the same place
+ var newScroll = activeMediaIndex * playerWidthPlusPadding
+ if (scrollIntoCurrentMedia > playerWidthPlusPadding) {
+ newScroll += playerWidthPlusPadding -
+ (scrollIntoCurrentMedia - playerWidthPlusPadding)
+ } else {
+ newScroll += scrollIntoCurrentMedia
+ }
+ scrollView.scrollX = newScroll
+ }
+
+ /**
+ * Does the dismiss currently show the setting cog?
+ */
+ var showsSettingsButton: Boolean = false
+
+ /**
+ * A utility to detect gestures, used in the touch listener
+ */
+ private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
+ override fun onFling(
+ eStart: MotionEvent?,
+ eCurrent: MotionEvent?,
+ vX: Float,
+ vY: Float
+ ) = onFling(vX, vY)
+
+ override fun onScroll(
+ down: MotionEvent?,
+ lastMotion: MotionEvent?,
+ distanceX: Float,
+ distanceY: Float
+ ) = onScroll(down!!, lastMotion!!, distanceX)
+
+ override fun onDown(e: MotionEvent?): Boolean {
+ if (falsingProtectionNeeded) {
+ falsingManager.onNotificationStartDismissing()
+ }
+ return false
+ }
+ }
+
+ /**
+ * The touch listener for the scroll view
+ */
+ private val touchListener = object : Gefingerpoken {
+ override fun onTouchEvent(motionEvent: MotionEvent?) = onTouch(motionEvent!!)
+ override fun onInterceptTouchEvent(ev: MotionEvent?) = onInterceptTouch(ev!!)
+ }
+
+ /**
+ * A listener that is invoked when the scrolling changes to update player visibilities
+ */
+ private val scrollChangedListener = object : View.OnScrollChangeListener {
+ override fun onScrollChange(
+ v: View?,
+ scrollX: Int,
+ scrollY: Int,
+ oldScrollX: Int,
+ oldScrollY: Int
+ ) {
+ if (playerWidthPlusPadding == 0) {
+ return
+ }
+ onMediaScrollingChanged(scrollX / playerWidthPlusPadding,
+ scrollX % playerWidthPlusPadding)
+ }
+ }
+
+ init {
+ gestureDetector = GestureDetectorCompat(scrollView.context, gestureListener)
+ scrollView.touchListener = touchListener
+ scrollView.setOverScrollMode(View.OVER_SCROLL_NEVER)
+ mediaContent = scrollView.contentContainer
+ scrollView.setOnScrollChangeListener(scrollChangedListener)
+ scrollView.outlineProvider = object : ViewOutlineProvider() {
+ override fun getOutline(view: View?, outline: Outline?) {
+ outline?.setRoundRect(0, 0, carouselWidth, carouselHeight, cornerRadius.toFloat())
+ }
+ }
+ }
+
+ fun onSettingsButtonUpdated(button: View) {
+ settingsButton = button
+ // We don't have a context to resolve, lets use the settingsbuttons one since that is
+ // reinflated appropriately
+ cornerRadius = settingsButton.resources.getDimensionPixelSize(
+ Utils.getThemeAttr(settingsButton.context, android.R.attr.dialogCornerRadius))
+ updateSettingsPresentation()
+ scrollView.invalidateOutline()
+ }
+
+ private fun updateSettingsPresentation() {
+ if (showsSettingsButton) {
+ val settingsOffset = MathUtils.map(
+ 0.0f,
+ getMaxTranslation().toFloat(),
+ 0.0f,
+ 1.0f,
+ Math.abs(contentTranslation))
+ val settingsTranslation = (1.0f - settingsOffset) * -settingsButton.width *
+ SETTINGS_BUTTON_TRANSLATION_FRACTION
+ val newTranslationX: Float
+ if (contentTranslation > 0) {
+ newTranslationX = settingsTranslation
+ } else {
+ newTranslationX = scrollView.width - settingsTranslation - settingsButton.width
+ }
+ val rotation = (1.0f - settingsOffset) * 50
+ settingsButton.rotation = rotation * -Math.signum(contentTranslation)
+ val alpha = MathUtils.map(0.5f, 1.0f, 0.0f, 1.0f, settingsOffset)
+ settingsButton.alpha = alpha
+ settingsButton.visibility = if (alpha != 0.0f) View.VISIBLE else View.INVISIBLE
+ settingsButton.translationX = newTranslationX
+ settingsButton.translationY = (scrollView.height - settingsButton.height) / 2.0f
+ } else {
+ settingsButton.visibility = View.INVISIBLE
+ }
+ }
+
+ private fun onTouch(motionEvent: MotionEvent): Boolean {
+ val isUp = motionEvent.action == MotionEvent.ACTION_UP
+ if (isUp && falsingProtectionNeeded) {
+ falsingManager.onNotificationStopDismissing()
+ }
+ if (gestureDetector.onTouchEvent(motionEvent)) {
+ if (isUp) {
+ // If this is an up and we're flinging, we don't want to have this touch reach
+ // the view, otherwise that would scroll, while we are trying to snap to the
+ // new page. Let's dispatch a cancel instead.
+ scrollView.cancelCurrentScroll()
+ return true
+ } else {
+ // Pass touches to the scrollView
+ return false
+ }
+ }
+ if (isUp || motionEvent.action == MotionEvent.ACTION_CANCEL) {
+ // It's an up and the fling didn't take it above
+ val pos = scrollView.scrollX % playerWidthPlusPadding
+ val scollXAmount: Int
+ if (pos > playerWidthPlusPadding / 2) {
+ scollXAmount = playerWidthPlusPadding - pos
+ } else {
+ scollXAmount = -1 * pos
+ }
+ if (scollXAmount != 0) {
+ // Delay the scrolling since scrollView calls springback which cancels
+ // the animation again..
+ mainExecutor.execute {
+ scrollView.smoothScrollBy(scollXAmount, 0)
+ }
+ }
+ val currentTranslation = scrollView.getContentTranslation()
+ if (currentTranslation != 0.0f) {
+ // We started a Swipe but didn't end up with a fling. Let's either go to the
+ // dismissed position or go back.
+ val springBack = Math.abs(currentTranslation) < getMaxTranslation() / 2
+ || isFalseTouch()
+ val newTranslation: Float
+ if (springBack) {
+ newTranslation = 0.0f
+ } else {
+ newTranslation = getMaxTranslation() * Math.signum(currentTranslation)
+ if (!showsSettingsButton) {
+ // Delay the dismiss a bit to avoid too much overlap. Waiting until the
+ // animation has finished also feels a bit too slow here.
+ mainExecutor.executeDelayed({
+ dismissCallback.invoke()
+ }, DISMISS_DELAY)
+ }
+ }
+ PhysicsAnimator.getInstance(this).spring(CONTENT_TRANSLATION,
+ newTranslation, startVelocity = 0.0f, config = translationConfig).start()
+ scrollView.animationTargetX = newTranslation
+ }
+ }
+ // Always pass touches to the scrollView
+ return false
+ }
+
+ private fun isFalseTouch() = falsingProtectionNeeded && falsingManager.isFalseTouch
+
+ private fun getMaxTranslation() = if (showsSettingsButton) {
+ settingsButton.width
+ } else {
+ playerWidthPlusPadding
+ }
+
+ private fun onInterceptTouch(motionEvent: MotionEvent): Boolean {
+ return gestureDetector.onTouchEvent(motionEvent)
+ }
+
+ fun onScroll(down: MotionEvent,
+ lastMotion: MotionEvent,
+ distanceX: Float): Boolean {
+ val totalX = lastMotion.x - down.x
+ val currentTranslation = scrollView.getContentTranslation()
+ if (currentTranslation != 0.0f ||
+ !scrollView.canScrollHorizontally((-totalX).toInt())) {
+ var newTranslation = currentTranslation - distanceX
+ val absTranslation = Math.abs(newTranslation)
+ if (absTranslation > getMaxTranslation()) {
+ // Rubberband all translation above the maximum
+ if (Math.signum(distanceX) != Math.signum(currentTranslation)) {
+ // The movement is in the same direction as our translation,
+ // Let's rubberband it.
+ if (Math.abs(currentTranslation) > getMaxTranslation()) {
+ // we were already overshooting before. Let's add the distance
+ // fully rubberbanded.
+ newTranslation = currentTranslation - distanceX * RUBBERBAND_FACTOR
+ } else {
+ // We just crossed the boundary, let's rubberband it all
+ newTranslation = Math.signum(newTranslation) * (getMaxTranslation() +
+ (absTranslation - getMaxTranslation()) * RUBBERBAND_FACTOR)
+ }
+ } // Otherwise we don't have do do anything, and will remove the unrubberbanded
+ // translation
+ }
+ if (Math.signum(newTranslation) != Math.signum(currentTranslation)
+ && currentTranslation != 0.0f) {
+ // We crossed the 0.0 threshold of the translation. Let's see if we're allowed
+ // to scroll into the new direction
+ if (scrollView.canScrollHorizontally(-newTranslation.toInt())) {
+ // We can actually scroll in the direction where we want to translate,
+ // Let's make sure to stop at 0
+ newTranslation = 0.0f
+ }
+ }
+ val physicsAnimator = PhysicsAnimator.getInstance(this)
+ if (physicsAnimator.isRunning()) {
+ physicsAnimator.spring(CONTENT_TRANSLATION,
+ newTranslation, startVelocity = 0.0f, config = translationConfig).start()
+ } else {
+ contentTranslation = newTranslation
+ }
+ scrollView.animationTargetX = newTranslation
+ return true
+ }
+ return false
+ }
+
+ private fun onFling(
+ vX: Float,
+ vY: Float
+ ): Boolean {
+ if (vX * vX < 0.5 * vY * vY) {
+ return false
+ }
+ if (vX * vX < FLING_SLOP) {
+ return false
+ }
+ val currentTranslation = scrollView.getContentTranslation()
+ if (currentTranslation != 0.0f) {
+ // We're translated and flung. Let's see if the fling is in the same direction
+ val newTranslation: Float
+ if (Math.signum(vX) != Math.signum(currentTranslation) || isFalseTouch()) {
+ // The direction of the fling isn't the same as the translation, let's go to 0
+ newTranslation = 0.0f
+ } else {
+ newTranslation = getMaxTranslation() * Math.signum(currentTranslation)
+ // Delay the dismiss a bit to avoid too much overlap. Waiting until the animation
+ // has finished also feels a bit too slow here.
+ if (!showsSettingsButton) {
+ mainExecutor.executeDelayed({
+ dismissCallback.invoke()
+ }, DISMISS_DELAY)
+ }
+ }
+ PhysicsAnimator.getInstance(this).spring(CONTENT_TRANSLATION,
+ newTranslation, startVelocity = vX, config = translationConfig).start()
+ scrollView.animationTargetX = newTranslation
+ } else {
+ // We're flinging the player! Let's go either to the previous or to the next player
+ val pos = scrollView.scrollX
+ val currentIndex = if (playerWidthPlusPadding > 0) pos / playerWidthPlusPadding else 0
+ var destIndex = if (vX <= 0) currentIndex + 1 else currentIndex
+ destIndex = Math.max(0, destIndex)
+ destIndex = Math.min(mediaContent.getChildCount() - 1, destIndex)
+ val view = mediaContent.getChildAt(destIndex)
+ // We need to post this since we're dispatching a touch to the underlying view to cancel
+ // but canceling will actually abort the animation.
+ mainExecutor.execute {
+ scrollView.smoothScrollTo(view.left, scrollView.scrollY)
+ }
+ }
+ return true
+ }
+
+ /**
+ * Reset the translation of the players when swiped
+ */
+ fun resetTranslation(animate: Boolean = false) {
+ if (scrollView.getContentTranslation() != 0.0f) {
+ if (animate) {
+ PhysicsAnimator.getInstance(this).spring(CONTENT_TRANSLATION,
+ 0.0f, config = translationConfig).start()
+ scrollView.animationTargetX = 0.0f
+ } else {
+ PhysicsAnimator.getInstance(this).cancel()
+ contentTranslation = 0.0f
+ }
+ }
+ }
+
+ private fun updateClipToOutline() {
+ val clip = contentTranslation != 0.0f || scrollIntoCurrentMedia != 0
+ scrollView.clipToOutline = clip
+ }
+
+ private fun onMediaScrollingChanged(newIndex: Int, scrollInAmount: Int) {
+ val wasScrolledIn = scrollIntoCurrentMedia != 0
+ scrollIntoCurrentMedia = scrollInAmount
+ val nowScrolledIn = scrollIntoCurrentMedia != 0
+ if (newIndex != activeMediaIndex || wasScrolledIn != nowScrolledIn) {
+ activeMediaIndex = newIndex
+ updatePlayerVisibilities()
+ }
+ val location = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
+ scrollInAmount.toFloat() / playerWidthPlusPadding else 0f
+ pageIndicator.setLocation(location)
+ updateClipToOutline()
+ }
+
+ /**
+ * Notified whenever the players or their order has changed
+ */
+ fun onPlayersChanged() {
+ updatePlayerVisibilities()
+ updateMediaPaddings()
+ }
+
+ private fun updateMediaPaddings() {
+ val padding = scrollView.context.resources.getDimensionPixelSize(R.dimen.qs_media_padding)
+ val childCount = mediaContent.childCount
+ for (i in 0 until childCount) {
+ val mediaView = mediaContent.getChildAt(i)
+ val desiredPaddingEnd = if (i == childCount - 1) 0 else padding
+ val layoutParams = mediaView.layoutParams as ViewGroup.MarginLayoutParams
+ if (layoutParams.marginEnd != desiredPaddingEnd) {
+ layoutParams.marginEnd = desiredPaddingEnd
+ mediaView.layoutParams = layoutParams
+ }
+ }
+ }
+
+ private fun updatePlayerVisibilities() {
+ val scrolledIn = scrollIntoCurrentMedia != 0
+ for (i in 0 until mediaContent.childCount) {
+ val view = mediaContent.getChildAt(i)
+ val visible = (i == activeMediaIndex) || ((i == (activeMediaIndex + 1)) && scrolledIn)
+ view.visibility = if (visible) View.VISIBLE else View.INVISIBLE
+ }
+ }
+
+ /**
+ * Notify that a player will be removed right away. This gives us the opporunity to look
+ * where it was and update our scroll position.
+ */
+ fun onPrePlayerRemoved(removed: MediaControlPanel) {
+ val beforeActive = mediaContent.indexOfChild(removed.view?.player) <= activeMediaIndex
+ if (beforeActive) {
+ // also update the index here since the scroll below might not always lead
+ // to a scrolling changed
+ activeMediaIndex = Math.max(0, activeMediaIndex - 1)
+ scrollView.scrollX = Math.max(scrollView.scrollX -
+ playerWidthPlusPadding, 0)
+ }
+ }
+
+ /**
+ * Update the bounds of the carousel
+ */
+ fun setCarouselBounds(currentCarouselWidth: Int, currentCarouselHeight: Int) {
+ if (currentCarouselHeight != carouselHeight || currentCarouselWidth != carouselHeight) {
+ carouselWidth = currentCarouselWidth
+ carouselHeight = currentCarouselHeight
+ scrollView.invalidateOutline()
+ }
+ }
+
+ companion object {
+ private val CONTENT_TRANSLATION = object : FloatPropertyCompat<MediaCarouselScrollHandler>(
+ "contentTranslation") {
+ override fun getValue(handler: MediaCarouselScrollHandler): Float {
+ return handler.contentTranslation
+ }
+
+ override fun setValue(handler: MediaCarouselScrollHandler, value: Float) {
+ handler.contentTranslation = value
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index c80c82f..3fc162e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -20,13 +20,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
-import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon;
-import android.graphics.drawable.RippleDrawable;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
@@ -60,6 +57,7 @@
*/
public class MediaControlPanel {
private static final String TAG = "MediaControlPanel";
+ private static final float DISABLED_ALPHA = 0.38f;
// Button IDs for QS controls
static final int[] ACTION_IDS = {
@@ -258,12 +256,6 @@
ImageView iconView = mViewHolder.getSeamlessIcon();
TextView deviceName = mViewHolder.getSeamlessText();
- // Update the outline color
- RippleDrawable bkgDrawable = (RippleDrawable) mViewHolder.getSeamless().getForeground();
- GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0);
- rect.setStroke(2, deviceName.getCurrentTextColor());
- rect.setColor(Color.TRANSPARENT);
-
final MediaDeviceData device = data.getDevice();
final int seamlessId = mViewHolder.getSeamless().getId();
final int seamlessFallbackId = mViewHolder.getSeamlessFallback().getId();
@@ -276,6 +268,11 @@
mViewHolder.getSeamless().setVisibility(seamlessVisibility);
expandedSet.setVisibility(seamlessId, seamlessVisibility);
collapsedSet.setVisibility(seamlessId, seamlessVisibility);
+ final float seamlessAlpha = data.getResumption() ? DISABLED_ALPHA : 1.0f;
+ expandedSet.setAlpha(seamlessId, seamlessAlpha);
+ collapsedSet.setAlpha(seamlessId, seamlessAlpha);
+ // Disable clicking on output switcher for resumption controls.
+ mViewHolder.getSeamless().setEnabled(!data.getResumption());
if (showFallback) {
iconView.setImageDrawable(null);
deviceName.setText(null);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 0b0ffce..8c9cb1b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -81,6 +81,11 @@
*/
var resumeAction: Runnable?,
/**
+ * Indicates that this player is a resumption player (ie. It only shows a play actions which
+ * will start the app and start playing).
+ */
+ var resumption: Boolean = false,
+ /**
* Notification key for cancelling a media player after a timeout (when not using resumption.)
*/
val notificationKey: String? = null,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index c59a548..416c81a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -323,7 +323,7 @@
onMediaDataLoaded(packageName, null, MediaData(true, bgColor, appName,
null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
packageName, token, appIntent, device = null, active = false,
- resumeAction = resumeAction, notificationKey = packageName,
+ resumeAction = resumeAction, resumption = true, notificationKey = packageName,
hasCheckedForResume = true))
}
}
@@ -542,7 +542,7 @@
val data = mediaEntries.remove(key)!!
val resumeAction = getResumeMediaAction(data.resumeAction!!)
val updated = data.copy(token = null, actions = listOf(resumeAction),
- actionsToShowInCompact = listOf(0), active = false)
+ actionsToShowInCompact = listOf(0), active = false, resumption = true)
mediaEntries.put(data.packageName, updated)
// Notify listeners of "new" controls
val listenersCopy = listeners.toSet()
@@ -592,6 +592,16 @@
}
}
+ /**
+ * Invoked when the user has dismissed the media carousel
+ */
+ fun onSwipeToDismiss() {
+ val mediaKeys = mediaEntries.keys.toSet()
+ mediaKeys.forEach {
+ setTimedOut(it, timedOut = true)
+ }
+ }
+
interface Listener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 26fa296..3266074 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -49,7 +49,7 @@
private val statusBarStateController: SysuiStatusBarStateController,
private val keyguardStateController: KeyguardStateController,
private val bypassController: KeyguardBypassController,
- private val mediaViewManager: MediaViewManager,
+ private val mediaCarouselController: MediaCarouselController,
private val notifLockscreenUserManager: NotificationLockscreenUserManager,
wakefulnessLifecycle: WakefulnessLifecycle
) {
@@ -65,7 +65,7 @@
private var animationStartBounds: Rect = Rect()
private var targetBounds: Rect = Rect()
private val mediaFrame
- get() = mediaViewManager.mediaFrame
+ get() = mediaCarouselController.mediaFrame
private var statusbarState: Int = statusBarStateController.state
private var animator = ValueAnimator.ofFloat(0.0f, 1.0f).apply {
interpolator = Interpolators.FAST_OUT_SLOW_IN
@@ -227,6 +227,10 @@
fun register(mediaObject: MediaHost): UniqueObjectHostView {
val viewHost = createUniqueObjectHost()
mediaObject.hostView = viewHost
+ mediaObject.addVisibilityChangeListener {
+ // Never animate because of a visibility change, only state changes should do that
+ updateDesiredLocation(forceNoAnimation = true)
+ }
mediaHosts[mediaObject.location] = mediaObject
if (mediaObject.location == desiredLocation) {
// In case we are overriding a view that is already visible, make sure we attach it
@@ -260,8 +264,10 @@
/**
* Updates the location that the view should be in. If it changes, an animation may be triggered
* going from the old desired location to the new one.
+ *
+ * @param forceNoAnimation optional parameter telling the system not to animate
*/
- private fun updateDesiredLocation() {
+ private fun updateDesiredLocation(forceNoAnimation: Boolean = false) {
val desiredLocation = calculateLocation()
if (desiredLocation != this.desiredLocation) {
if (this.desiredLocation >= 0) {
@@ -270,11 +276,12 @@
val isNewView = this.desiredLocation == -1
this.desiredLocation = desiredLocation
// Let's perform a transition
- val animate = shouldAnimateTransition(desiredLocation, previousLocation)
+ val animate = !forceNoAnimation &&
+ shouldAnimateTransition(desiredLocation, previousLocation)
val (animDuration, delay) = getAnimationParams(previousLocation, desiredLocation)
val host = getHost(desiredLocation)
- mediaViewManager.onDesiredLocationChanged(desiredLocation, host, animate, animDuration,
- delay)
+ mediaCarouselController.onDesiredLocationChanged(desiredLocation, host, animate,
+ animDuration, delay)
performTransitionToNewLocation(isNewView, animate)
}
}
@@ -457,7 +464,7 @@
val startLocation = if (currentlyInGuidedTransformation) previousLocation else -1
val progress = if (currentlyInGuidedTransformation) getTransformationProgress() else 1.0f
val endLocation = desiredLocation
- mediaViewManager.setCurrentState(startLocation, endLocation, progress, immediately)
+ mediaCarouselController.setCurrentState(startLocation, endLocation, progress, immediately)
updateHostAttachment()
if (currentAttachmentLocation == IN_OVERLAY) {
mediaFrame.setLeftTopRightBottom(
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 7c5f0d1..1ae9d3f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -2,6 +2,7 @@
import android.graphics.PointF
import android.graphics.Rect
+import android.util.ArraySet
import android.view.View
import android.view.View.OnAttachStateChangeListener
import com.android.systemui.util.animation.MeasurementInput
@@ -20,7 +21,7 @@
lateinit var hostView: UniqueObjectHostView
var location: Int = -1
private set
- var visibleChangedListener: ((Boolean) -> Unit)? = null
+ private var visibleChangedListeners: ArraySet<(Boolean) -> Unit> = ArraySet()
private val tmpLocationOnScreen: IntArray = intArrayOf(0, 0)
@@ -58,6 +59,10 @@
}
}
+ fun addVisibilityChangeListener(listener: (Boolean) -> Unit) {
+ visibleChangedListeners.add(listener)
+ }
+
/**
* Initialize this MediaObject and create a host view.
* All state should already be set on this host before calling this method in order to avoid
@@ -113,8 +118,13 @@
} else {
mediaDataManager.hasAnyMedia()
}
- hostView.visibility = if (visible) View.VISIBLE else View.GONE
- visibleChangedListener?.invoke(visible)
+ val newVisibility = if (visible) View.VISIBLE else View.GONE
+ if (newVisibility != hostView.visibility) {
+ hostView.visibility = newVisibility
+ visibleChangedListeners.forEach {
+ it.invoke(visible)
+ }
+ }
}
class MediaHostStateHolder @Inject constructor() : MediaHostState {
@@ -153,6 +163,15 @@
changedListener?.invoke()
}
+ override var falsingProtectionNeeded: Boolean = false
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ changedListener?.invoke()
+ }
+
override fun getPivotX(): Float = gonePivot.x
override fun getPivotY(): Float = gonePivot.y
override fun setGonePivot(x: Float, y: Float) {
@@ -178,6 +197,7 @@
mediaHostState.measurementInput = measurementInput?.copy()
mediaHostState.visible = visible
mediaHostState.gonePivot.set(gonePivot)
+ mediaHostState.falsingProtectionNeeded = falsingProtectionNeeded
return mediaHostState
}
@@ -197,6 +217,9 @@
if (visible != other.visible) {
return false
}
+ if (falsingProtectionNeeded != other.falsingProtectionNeeded) {
+ return false
+ }
if (!gonePivot.equals(other.getPivotX(), other.getPivotY())) {
return false
}
@@ -206,6 +229,7 @@
override fun hashCode(): Int {
var result = measurementInput?.hashCode() ?: 0
result = 31 * result + expansion.hashCode()
+ result = 31 * result + falsingProtectionNeeded.hashCode()
result = 31 * result + showsOnlyActiveMedia.hashCode()
result = 31 * result + if (visible) 1 else 2
result = 31 * result + gonePivot.hashCode()
@@ -239,6 +263,11 @@
var visible: Boolean
/**
+ * Does this host need any falsing protection?
+ */
+ var falsingProtectionNeeded: Boolean
+
+ /**
* Sets the pivot point when clipping the height or width.
* Clipping happens when animating visibility when we're visible in QS but not on QQS,
* for example.
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaScrollView.kt b/packages/SystemUI/src/com/android/systemui/media/MediaScrollView.kt
new file mode 100644
index 0000000..a079b06
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaScrollView.kt
@@ -0,0 +1,100 @@
+package com.android.systemui.media
+
+import android.content.Context
+import android.os.SystemClock
+import android.util.AttributeSet
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.ViewGroup
+import android.widget.HorizontalScrollView
+import com.android.systemui.Gefingerpoken
+import com.android.systemui.util.animation.physicsAnimator
+
+/**
+ * A ScrollView used in Media that doesn't limit itself to the childs bounds. This is useful
+ * when only measuring children but not the parent, when trying to apply a new scroll position
+ */
+class MediaScrollView @JvmOverloads constructor(
+ context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
+ : HorizontalScrollView(context, attrs, defStyleAttr) {
+
+ lateinit var contentContainer: ViewGroup
+ private set
+ var touchListener: Gefingerpoken? = null
+
+ /**
+ * The target value of the translation X animation. Only valid if the physicsAnimator is running
+ */
+ var animationTargetX = 0.0f
+
+ /**
+ * Get the current content translation. This is usually the normal translationX of the content,
+ * but when animating, it might differ
+ */
+ fun getContentTranslation() = if (contentContainer.physicsAnimator.isRunning()) {
+ animationTargetX
+ } else {
+ contentContainer.translationX
+ }
+
+ /**
+ * Allow all scrolls to go through, use base implementation
+ */
+ override fun scrollTo(x: Int, y: Int) {
+ if (mScrollX != x || mScrollY != y) {
+ val oldX: Int = mScrollX
+ val oldY: Int = mScrollY
+ mScrollX = x
+ mScrollY = y
+ invalidateParentCaches()
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY)
+ if (!awakenScrollBars()) {
+ postInvalidateOnAnimation()
+ }
+ }
+ }
+
+ override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
+ var intercept = false;
+ touchListener?.let {
+ intercept = it.onInterceptTouchEvent(ev)
+ }
+ return super.onInterceptTouchEvent(ev) || intercept;
+ }
+
+ override fun onTouchEvent(ev: MotionEvent?): Boolean {
+ var touch = false;
+ touchListener?.let {
+ touch = it.onTouchEvent(ev)
+ }
+ return super.onTouchEvent(ev) || touch
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ contentContainer = getChildAt(0) as ViewGroup
+ }
+
+ override fun overScrollBy(deltaX: Int, deltaY: Int, scrollX: Int, scrollY: Int,
+ scrollRangeX: Int, scrollRangeY: Int, maxOverScrollX: Int,
+ maxOverScrollY: Int, isTouchEvent: Boolean): Boolean {
+ if (getContentTranslation() != 0.0f) {
+ // When we're dismissing we ignore all the scrolling
+ return false
+ }
+ return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
+ scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent)
+ }
+
+ /**
+ * Cancel the current touch event going on.
+ */
+ fun cancelCurrentScroll() {
+ val now = SystemClock.uptimeMillis()
+ val event = MotionEvent.obtain(now, now,
+ MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0)
+ event.source = InputDevice.SOURCE_TOUCHSCREEN
+ super.onTouchEvent(event)
+ event.recycle()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 90ccfc6..90961db 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -35,6 +35,10 @@
private val mediaHostStatesManager: MediaHostStatesManager
) {
+ /**
+ * A listener when the current dimensions of the player change
+ */
+ lateinit var sizeChangedListener: () -> Unit
private var firstRefresh: Boolean = true
private var transitionLayout: TransitionLayout? = null
private val layoutController = TransitionLayoutController()
@@ -76,6 +80,17 @@
private val tmpPoint = PointF()
/**
+ * The current width of the player. This might not factor in case the player is animating
+ * to the current state, but represents the end state
+ */
+ var currentWidth: Int = 0
+ /**
+ * The current height of the player. This might not factor in case the player is animating
+ * to the current state, but represents the end state
+ */
+ var currentHeight: Int = 0
+
+ /**
* A callback for media state changes
*/
val stateCallback = object : MediaHostStatesManager.Callback {
@@ -105,6 +120,11 @@
collapsedLayout.load(context, R.xml.media_collapsed)
expandedLayout.load(context, R.xml.media_expanded)
mediaHostStatesManager.addController(this)
+ layoutController.sizeChangedListener = { width: Int, height: Int ->
+ currentWidth = width
+ currentHeight = height
+ sizeChangedListener.invoke()
+ }
}
/**
@@ -279,6 +299,8 @@
tmpPoint, tmpState)
tmpState
}
+ currentWidth = result.width
+ currentHeight = result.height
layoutController.setState(result, applyImmediately, shouldAnimate, animationDuration,
animationDelay)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/UnboundHorizontalScrollView.kt b/packages/SystemUI/src/com/android/systemui/media/UnboundHorizontalScrollView.kt
deleted file mode 100644
index 8efc954..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/UnboundHorizontalScrollView.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.android.systemui.media
-
-import android.content.Context
-import android.util.AttributeSet
-import android.widget.HorizontalScrollView
-
-/**
- * A Horizontal scrollview that doesn't limit itself to the childs bounds. This is useful
- * when only measuring children but not the parent, when trying to apply a new scroll position
- */
-class UnboundHorizontalScrollView @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
- : HorizontalScrollView(context, attrs, defStyleAttr) {
-
- /**
- * Allow all scrolls to go through, use base implementation
- */
- override fun scrollTo(x: Int, y: Int) {
- if (mScrollX != x || mScrollY != y) {
- val oldX: Int = mScrollX
- val oldY: Int = mScrollY
- mScrollX = x
- mScrollY = y
- invalidateParentCaches()
- onScrollChanged(mScrollX, mScrollY, oldX, oldY)
- if (!awakenScrollBars()) {
- postInvalidateOnAnimation()
- }
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
index aa17c4a..309b32f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
@@ -46,6 +46,11 @@
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
+ } else if (!canScrollVertically(-1)) {
+ // Don't pass on the touch to the view, because scrolling will unconditionally
+ // disallow interception even if we can't scroll.
+ // if a user can't scroll at all, we should never listen to the touch.
+ return false;
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index bc8f5a8..e66b33c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -18,7 +18,6 @@
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnLayoutChangeListener;
-import android.widget.ScrollView;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.qs.QS;
@@ -300,10 +299,16 @@
if (mQsPanel.getSecurityFooter() != null) {
builder.addFloat(mQsPanel.getSecurityFooter().getView(), "alpha", 0, 1);
}
+ if (mQsPanel.getDivider() != null) {
+ builder.addFloat(mQsPanel.getDivider(), "alpha", 0, 1);
+ }
mFirstPageDelayedAnimator = builder.build();
if (mQsPanel.getSecurityFooter() != null) {
mAllViews.add(mQsPanel.getSecurityFooter().getView());
}
+ if (mQsPanel.getDivider() != null) {
+ mAllViews.add(mQsPanel.getDivider());
+ }
float px = 0;
float py = 1;
if (tiles.size() <= 3) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 0332bc3..6b12e47 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -26,8 +26,12 @@
import android.view.View;
import android.widget.FrameLayout;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringForce;
+
import com.android.systemui.R;
import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.util.animation.PhysicsAnimator;
/**
* Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
@@ -35,7 +39,22 @@
public class QSContainerImpl extends FrameLayout {
private final Point mSizePoint = new Point();
+ private static final FloatPropertyCompat<QSContainerImpl> BACKGROUND_BOTTOM =
+ new FloatPropertyCompat<QSContainerImpl>("backgroundBottom") {
+ @Override
+ public float getValue(QSContainerImpl qsImpl) {
+ return qsImpl.getBackgroundBottom();
+ }
+ @Override
+ public void setValue(QSContainerImpl background, float value) {
+ background.setBackgroundBottom((int) value);
+ }
+ };
+ private static final PhysicsAnimator.SpringConfig BACKGROUND_SPRING
+ = new PhysicsAnimator.SpringConfig(SpringForce.STIFFNESS_MEDIUM,
+ SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+ private int mBackgroundBottom = -1;
private int mHeightOverride = -1;
private QSPanel mQSPanel;
private View mQSDetail;
@@ -53,6 +72,7 @@
private boolean mQsDisabled;
private int mContentPaddingStart = -1;
private int mContentPaddingEnd = -1;
+ private boolean mAnimateBottomOnNextLayout;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -71,10 +91,30 @@
mStatusBarBackground = findViewById(R.id.quick_settings_status_bar_background);
mBackgroundGradient = findViewById(R.id.quick_settings_gradient_view);
updateResources();
+ mHeader.getHeaderQsPanel().setMediaVisibilityChangedListener((visible) -> {
+ if (mHeader.getHeaderQsPanel().isShown()) {
+ mAnimateBottomOnNextLayout = true;
+ }
+ });
+
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
+ private void setBackgroundBottom(int value) {
+ // We're saving the bottom separately since otherwise the bottom would be overridden in
+ // the layout and the animation wouldn't properly start at the old position.
+ mBackgroundBottom = value;
+ mBackground.setBottom(value);
+ }
+
+ private float getBackgroundBottom() {
+ if (mBackgroundBottom == -1) {
+ return mBackground.getBottom();
+ }
+ return mBackgroundBottom;
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -140,7 +180,8 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- updateExpansion();
+ updateExpansion(mAnimateBottomOnNextLayout /* animate */);
+ mAnimateBottomOnNextLayout = false;
}
public void disable(int state1, int state2, boolean animate) {
@@ -181,13 +222,31 @@
}
public void updateExpansion() {
+ updateExpansion(false /* animate */);
+ }
+
+ public void updateExpansion(boolean animate) {
int height = calculateContainerHeight();
setBottom(getTop() + height);
mQSDetail.setBottom(getTop() + height);
// Pin the drag handle to the bottom of the panel.
mDragHandle.setTranslationY(height - mDragHandle.getHeight());
mBackground.setTop(mQSPanelContainer.getTop());
- mBackground.setBottom(height);
+ updateBackgroundBottom(height, animate);
+ }
+
+ private void updateBackgroundBottom(int height, boolean animated) {
+ PhysicsAnimator<QSContainerImpl> physicsAnimator = PhysicsAnimator.getInstance(this);
+ if (physicsAnimator.isPropertyAnimating(BACKGROUND_BOTTOM) || animated) {
+ // An animation is running or we want to animate
+ // Let's make sure to set the currentValue again, since the call below might only
+ // start in the next frame and otherwise we'd flicker
+ BACKGROUND_BOTTOM.setValue(this, BACKGROUND_BOTTOM.getValue(this));
+ physicsAnimator.spring(BACKGROUND_BOTTOM, height, BACKGROUND_SPRING).start();
+ } else {
+ BACKGROUND_BOTTOM.setValue(this, height);
+ }
+
}
protected int calculateContainerHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5021e00..c8a34f0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -65,6 +65,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -117,6 +118,8 @@
@Nullable
protected View mFooter;
+ @Nullable
+ protected View mDivider;
@Nullable
private ViewGroup mHeaderContainer;
@@ -141,6 +144,7 @@
private int mLastOrientation = -1;
private int mMediaTotalBottomMargin;
private int mFooterMarginStartHorizontal;
+ private Consumer<Boolean> mMediaVisibilityChangedListener;
@Inject
@@ -158,8 +162,8 @@
mMediaTotalBottomMargin = getResources().getDimensionPixelSize(
R.dimen.quick_settings_bottom_margin_media);
mMediaHost = mediaHost;
- mMediaHost.setVisibleChangedListener((visible) -> {
- switchTileLayout();
+ mMediaHost.addVisibilityChangeListener((visible) -> {
+ onMediaVisibilityChanged(visible);
return null;
});
mContext = context;
@@ -207,6 +211,13 @@
updateResources();
}
+ protected void onMediaVisibilityChanged(Boolean visible) {
+ switchTileLayout();
+ if (mMediaVisibilityChangedListener != null) {
+ mMediaVisibilityChangedListener.accept(visible);
+ }
+ }
+
protected void addSecurityFooter() {
mSecurityFooter = new QSSecurityFooter(this, mContext);
}
@@ -479,6 +490,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mFooter = findViewById(R.id.qs_footer);
+ mDivider = findViewById(R.id.divider);
switchTileLayout(true /* force */);
}
@@ -489,6 +501,13 @@
private boolean switchTileLayout(boolean force) {
/** Whether or not the QuickQSPanel currently contains a media player. */
boolean horizontal = shouldUseHorizontalLayout();
+ if (mDivider != null) {
+ if (!horizontal && mUsingMediaPlayer && mMediaHost.getVisible()) {
+ mDivider.setVisibility(View.VISIBLE);
+ } else {
+ mDivider.setVisibility(View.GONE);
+ }
+ }
if (horizontal != mUsingHorizontalLayout || force) {
mUsingHorizontalLayout = horizontal;
View visibleView = horizontal ? mHorizontalLinearLayout : (View) mRegularTileLayout;
@@ -522,6 +541,7 @@
}
updateTileLayoutMargins();
updateFooterMargin();
+ updateDividerMargin();
updateMediaHostContentMargins();
updateHorizontalLinearLayoutMargins();
updatePadding();
@@ -971,6 +991,11 @@
return mSecurityFooter;
}
+ @Nullable
+ public View getDivider() {
+ return mDivider;
+ }
+
public void showDeviceMonitoringDialog() {
if (mSecurityFooter != null) {
mSecurityFooter.showDeviceMonitoringDialog();
@@ -986,6 +1011,7 @@
mContentMarginEnd - mVisualTilePadding);
updateMediaHostContentMargins();
updateFooterMargin();
+ updateDividerMargin();
}
private void updateFooterMargin() {
@@ -1027,6 +1053,11 @@
updateMargins((View) mTileLayout, mVisualMarginStart, marginEnd);
}
+ private void updateDividerMargin() {
+ if (mDivider == null) return;
+ updateMargins(mDivider, mContentMarginStart, mContentMarginEnd);
+ }
+
/**
* Update the margins of the media hosts
*/
@@ -1065,6 +1096,10 @@
mHeaderContainer = headerContainer;
}
+ public void setMediaVisibilityChangedListener(Consumer<Boolean> visibilityChangedListener) {
+ mMediaVisibilityChangedListener = visibilityChangedListener;
+ }
+
private class H extends Handler {
private static final int SHOW_DETAIL = 1;
private static final int SET_TILE_VISIBILITY = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index d057a8a..8347def 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -97,6 +97,9 @@
mModes);
a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mOptions.setAdapter(a);
+ mOptions.setOnItemClickListenerInt((parent, view, position, id) -> {
+ mAudioSwitch.setChecked(true);
+ });
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 33f0f4d..9bbc4dd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -1105,14 +1105,19 @@
return;
}
- Intent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+ PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) {
ScreenshotNotificationsController.cancelScreenshotNotification(context);
}
ActivityOptions opts = ActivityOptions.makeBasic();
opts.setDisallowEnterPictureInPictureWhileLaunching(
intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
- context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
+ try {
+ actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent canceled", e);
+ }
+
};
if (mStatusBar != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 10e6902..e3fbdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -281,11 +281,13 @@
Intent.createChooser(sharingIntent, null, chooserAction.getIntentSender())
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, requestCode,
+ sharingChooserIntent, 0, null, UserHandle.CURRENT);
// Create a share action for the notification
PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
- .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, sharingChooserIntent)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
.putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
@@ -320,14 +322,17 @@
editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
+ editIntent, 0, null, UserHandle.CURRENT);
+
// Make sure pending intents for the system user are still unique across users
// by setting the (otherwise unused) request code to the current user id.
int requestCode = mContext.getUserId();
// Create a edit action
- PendingIntent editAction = PendingIntent.getBroadcast(context, requestCode,
+ PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
- .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, editIntent)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
.putExtra(GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION,
editIntent.getComponent() != null)
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
@@ -335,7 +340,7 @@
mSmartActionsEnabled)
.setAction(Intent.ACTION_EDIT)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
- PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_edit),
r.getString(com.android.internal.R.string.screenshot_edit), editAction);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 03a0d93..ad31220 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -387,6 +387,7 @@
}
// Always set this because we could be entering split when mMinimized is already true
wct.setFocusable(mSplits.mPrimary.token, !mMinimized);
+ boolean onlyFocusable = true;
// Update home-stack resizability
final boolean homeResizableChanged = mHomeStackResizable != homeStackResizable;
@@ -395,6 +396,7 @@
if (isDividerVisible()) {
WindowManagerProxy.applyHomeTasksMinimized(
mSplitLayout, mSplits.mSecondary.token, wct);
+ onlyFocusable = false;
}
}
@@ -416,7 +418,15 @@
}
}
updateTouchable();
- mWindowManagerProxy.applySyncTransaction(wct);
+ if (onlyFocusable) {
+ // If we are only setting focusability, a sync transaction isn't necessary (in fact it
+ // can interrupt other animations), so see if it can be submitted on pending instead.
+ if (!mSplits.mDivider.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
+ WindowOrganizer.applyTransaction(wct);
+ }
+ } else {
+ mWindowManagerProxy.applySyncTransaction(wct);
+ }
}
void setAdjustedForIme(boolean adjustedForIme) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
index d782a3ca..47c8c0a 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
@@ -29,6 +29,7 @@
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import android.window.WindowOrganizer;
import androidx.annotation.Nullable;
@@ -173,47 +174,51 @@
}
private void updateImeAdjustState() {
- // Reposition the server's secondary split position so that it evaluates
- // insets properly.
- WindowContainerTransaction wct = new WindowContainerTransaction();
- final SplitDisplayLayout splitLayout = getLayout();
- if (mTargetAdjusted) {
- splitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop);
- wct.setBounds(mSplits.mSecondary.token, splitLayout.mAdjustedSecondary);
- // "Freeze" the configuration size so that the app doesn't get a config
- // or relaunch. This is required because normally nav-bar contributes
- // to configuration bounds (via nondecorframe).
- Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration
- .windowConfiguration.getAppBounds());
- adjustAppBounds.offset(0, splitLayout.mAdjustedSecondary.top
- - splitLayout.mSecondary.top);
- wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds);
- wct.setScreenSizeDp(mSplits.mSecondary.token,
- mSplits.mSecondary.configuration.screenWidthDp,
- mSplits.mSecondary.configuration.screenHeightDp);
+ if (mAdjusted != mTargetAdjusted) {
+ // Reposition the server's secondary split position so that it evaluates
+ // insets properly.
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ final SplitDisplayLayout splitLayout = getLayout();
+ if (mTargetAdjusted) {
+ splitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop);
+ wct.setBounds(mSplits.mSecondary.token, splitLayout.mAdjustedSecondary);
+ // "Freeze" the configuration size so that the app doesn't get a config
+ // or relaunch. This is required because normally nav-bar contributes
+ // to configuration bounds (via nondecorframe).
+ Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration
+ .windowConfiguration.getAppBounds());
+ adjustAppBounds.offset(0, splitLayout.mAdjustedSecondary.top
+ - splitLayout.mSecondary.top);
+ wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds);
+ wct.setScreenSizeDp(mSplits.mSecondary.token,
+ mSplits.mSecondary.configuration.screenWidthDp,
+ mSplits.mSecondary.configuration.screenHeightDp);
- wct.setBounds(mSplits.mPrimary.token, splitLayout.mAdjustedPrimary);
- adjustAppBounds = new Rect(mSplits.mPrimary.configuration
- .windowConfiguration.getAppBounds());
- adjustAppBounds.offset(0, splitLayout.mAdjustedPrimary.top
- - splitLayout.mPrimary.top);
- wct.setAppBounds(mSplits.mPrimary.token, adjustAppBounds);
- wct.setScreenSizeDp(mSplits.mPrimary.token,
- mSplits.mPrimary.configuration.screenWidthDp,
- mSplits.mPrimary.configuration.screenHeightDp);
- } else {
- wct.setBounds(mSplits.mSecondary.token, splitLayout.mSecondary);
- wct.setAppBounds(mSplits.mSecondary.token, null);
- wct.setScreenSizeDp(mSplits.mSecondary.token,
- SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
- wct.setBounds(mSplits.mPrimary.token, splitLayout.mPrimary);
- wct.setAppBounds(mSplits.mPrimary.token, null);
- wct.setScreenSizeDp(mSplits.mPrimary.token,
- SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+ wct.setBounds(mSplits.mPrimary.token, splitLayout.mAdjustedPrimary);
+ adjustAppBounds = new Rect(mSplits.mPrimary.configuration
+ .windowConfiguration.getAppBounds());
+ adjustAppBounds.offset(0, splitLayout.mAdjustedPrimary.top
+ - splitLayout.mPrimary.top);
+ wct.setAppBounds(mSplits.mPrimary.token, adjustAppBounds);
+ wct.setScreenSizeDp(mSplits.mPrimary.token,
+ mSplits.mPrimary.configuration.screenWidthDp,
+ mSplits.mPrimary.configuration.screenHeightDp);
+ } else {
+ wct.setBounds(mSplits.mSecondary.token, splitLayout.mSecondary);
+ wct.setAppBounds(mSplits.mSecondary.token, null);
+ wct.setScreenSizeDp(mSplits.mSecondary.token,
+ SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+ wct.setBounds(mSplits.mPrimary.token, splitLayout.mPrimary);
+ wct.setAppBounds(mSplits.mPrimary.token, null);
+ wct.setScreenSizeDp(mSplits.mPrimary.token,
+ SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+ }
+
+ if (!mSplits.mDivider.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
+ WindowOrganizer.applyTransaction(wct);
+ }
}
- mSplits.mDivider.getWmProxy().applySyncTransaction(wct);
-
// Update all the adjusted-for-ime states
if (!mPaused) {
final DividerView view = getView();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 42d8c95..6f554e6 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -352,8 +352,6 @@
minimizeLeft + mMinimizedShadow.getMeasuredWidth(),
minimizeTop + mMinimizedShadow.getMeasuredHeight());
if (changed) {
- mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(),
- mHandle.getRight(), mHandle.getBottom()));
notifySplitScreenBoundsChanged();
}
}
@@ -679,6 +677,14 @@
private void notifySplitScreenBoundsChanged() {
mOtherTaskRect.set(mSplitLayout.mSecondary);
+ mTmpRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), mHandle.getBottom());
+ if (isHorizontalDivision()) {
+ mTmpRect.offsetTo(0, mDividerPositionY);
+ } else {
+ mTmpRect.offsetTo(mDividerPositionX, 0);
+ }
+ mWindowManagerProxy.setTouchRegion(mTmpRect);
+
mTmpRect.set(mSplitLayout.mDisplayLayout.stableInsets());
switch (mSplitLayout.getPrimarySplitSide()) {
case WindowManager.DOCKED_LEFT:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 8c7e071..a144453 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.UserInfo;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.hardware.biometrics.BiometricSourceType;
@@ -34,12 +35,16 @@
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.widget.ViewClippingUtil;
@@ -96,6 +101,7 @@
private final SettableWakeLock mWakeLock;
private final DockManager mDockManager;
private final DevicePolicyManager mDevicePolicyManager;
+ private final UserManager mUserManager;
private BroadcastReceiver mBroadcastReceiver;
private LockscreenLockIconController mLockIconController;
@@ -142,7 +148,8 @@
DockManager dockManager,
BroadcastDispatcher broadcastDispatcher,
DevicePolicyManager devicePolicyManager,
- IBatteryStats iBatteryStats) {
+ IBatteryStats iBatteryStats,
+ UserManager userManager) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mDevicePolicyManager = devicePolicyManager;
@@ -155,6 +162,7 @@
mWakeLock = new SettableWakeLock(
wakeLockBuilder.setTag("Doze:KeyguardIndication").build(), TAG);
mBatteryInfo = iBatteryStats;
+ mUserManager = userManager;
mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback());
mKeyguardUpdateMonitor.registerCallback(mTickReceiver);
@@ -180,8 +188,10 @@
updateDisclosure();
}
};
- mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, new IntentFilter(
- DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, intentFilter);
}
}
@@ -223,9 +233,8 @@
private void updateDisclosure() {
// NOTE: Because this uses IPC, avoid calling updateDisclosure() on a critical path.
- if (whitelistIpcs(mDevicePolicyManager::isDeviceManaged)) {
- final CharSequence organizationName =
- mDevicePolicyManager.getDeviceOwnerOrganizationName();
+ if (whitelistIpcs(this::isOrganizationOwnedDevice)) {
+ CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName();
if (organizationName != null) {
mDisclosure.switchIndication(mContext.getResources().getString(
R.string.do_disclosure_with_name, organizationName));
@@ -238,6 +247,38 @@
}
}
+ private boolean isOrganizationOwnedDevice() {
+ return mDevicePolicyManager.isDeviceManaged()
+ || mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile();
+ }
+
+ @Nullable
+ private CharSequence getOrganizationOwnedDeviceOrganizationName() {
+ if (mDevicePolicyManager.isDeviceManaged()) {
+ return mDevicePolicyManager.getDeviceOwnerOrganizationName();
+ } else if (mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
+ return getWorkProfileOrganizationName();
+ }
+ return null;
+ }
+
+ private CharSequence getWorkProfileOrganizationName() {
+ final int profileId = getWorkProfileUserId(UserHandle.myUserId());
+ if (profileId == UserHandle.USER_NULL) {
+ return null;
+ }
+ return mDevicePolicyManager.getOrganizationNameForUser(profileId);
+ }
+
+ private int getWorkProfileUserId(int userId) {
+ for (final UserInfo userInfo : mUserManager.getProfiles(userId)) {
+ if (userInfo.isManagedProfile()) {
+ return userInfo.id;
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
public void setVisible(boolean visible) {
mVisible = visible;
mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
index 9a25c48..c147023 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
@@ -16,6 +16,13 @@
package com.android.systemui.statusbar.notification.logging;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_ALERTING;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_FOREGROUND_SERVICE;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_HEADS_UP;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_MEDIA_CONTROLS;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_PEOPLE;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
+
import android.annotation.Nullable;
import android.service.notification.StatusBarNotification;
@@ -23,6 +30,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.nano.Notifications;
+import com.android.systemui.statusbar.notification.stack.PriorityBucket;
import java.util.List;
/**
@@ -84,7 +92,7 @@
if (n.getNotification() != null) {
proto.isGroupSummary = n.getNotification().isGroupSummary();
}
- proto.section = 1 + ne.getBucket(); // We want 0 to mean not set / unknown
+ proto.section = toNotificationSection(ne.getBucket());
proto_array[i] = proto;
}
++i;
@@ -92,4 +100,25 @@
notificationList.notifications = proto_array;
return notificationList;
}
+
+ /**
+ * Maps PriorityBucket enum to Notification.SECTION constant. The two lists should generally
+ * use matching names, but the values may differ, because PriorityBucket order changes from
+ * time to time, while logs need to have stable meanings.
+ * @param bucket PriorityBucket constant
+ * @return Notification.SECTION constant
+ */
+ static int toNotificationSection(@PriorityBucket int bucket) {
+ switch(bucket) {
+ case BUCKET_MEDIA_CONTROLS : return Notifications.Notification.SECTION_MEDIA_CONTROLS;
+ case BUCKET_HEADS_UP: return Notifications.Notification.SECTION_HEADS_UP;
+ case BUCKET_FOREGROUND_SERVICE:
+ return Notifications.Notification.SECTION_FOREGROUND_SERVICE;
+ case BUCKET_PEOPLE: return Notifications.Notification.SECTION_PEOPLE;
+ case BUCKET_ALERTING: return Notifications.Notification.SECTION_ALERTING;
+ case BUCKET_SILENT: return Notifications.Notification.SECTION_SILENT;
+ }
+ return Notifications.Notification.SECTION_UNKNOWN;
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
index 552a5fb..c2ab275 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
@@ -33,13 +33,16 @@
optional bool is_group_summary = 5;
// The section of the shade that the notification is in.
- // See NotificationSectionsManager.PriorityBucket.
+ // Sections follow NotificationSectionsManager.PriorityBucket but enum constants do not,
+ // as PriorityBucket order changes from time to time, while logs need to have stable meanings.
enum NotificationSection {
SECTION_UNKNOWN = 0;
SECTION_HEADS_UP = 1;
- SECTION_PEOPLE = 2;
- SECTION_ALERTING = 3;
- SECTION_SILENT = 4;
+ SECTION_MEDIA_CONTROLS = 2;
+ SECTION_PEOPLE = 3;
+ SECTION_ALERTING = 4;
+ SECTION_SILENT = 5;
+ SECTION_FOREGROUND_SERVICE = 6;
}
optional NotificationSection section = 6;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 7ed8350..ccfd8a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -769,10 +769,6 @@
return mContentTranslation;
}
- public boolean wantsAddAndRemoveAnimations() {
- return true;
- }
-
/** Sets whether this view is the first notification in a section. */
public void setFirstInSection(boolean firstInSection) {
mFirstInSection = firstInSection;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
index 383f2a2..040f707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
@@ -50,9 +50,4 @@
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
}
-
- @Override
- public boolean wantsAddAndRemoveAnimations() {
- return false;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index f30e49a..b9d31a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -97,6 +97,7 @@
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
@@ -200,6 +201,7 @@
private final KeyguardBypassController mKeyguardBypassController;
private final DynamicPrivacyController mDynamicPrivacyController;
private final SysuiStatusBarStateController mStatusbarStateController;
+ private final KeyguardMediaController mKeyguardMediaController;
private ExpandHelper mExpandHelper;
private final NotificationSwipeHelper mSwipeHelper;
@@ -533,6 +535,7 @@
private float mLastSentAppear;
private float mLastSentExpandedHeight;
private boolean mWillExpand;
+ private int mGapHeight;
private int mWaterfallTopInset;
@@ -552,6 +555,7 @@
SysuiStatusBarStateController statusBarStateController,
HeadsUpManagerPhone headsUpManager,
KeyguardBypassController keyguardBypassController,
+ KeyguardMediaController keyguardMediaController,
FalsingManager falsingManager,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGutsManager notificationGutsManager,
@@ -670,6 +674,16 @@
initializeForegroundServiceSection(fgsFeatureController);
mUiEventLogger = uiEventLogger;
mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
+ mKeyguardMediaController = keyguardMediaController;
+ keyguardMediaController.setVisibilityChangedListener((visible) -> {
+ if (visible) {
+ generateAddAnimation(keyguardMediaController.getView(), false /*fromMoreCard */);
+ } else {
+ generateRemoveAnimation(keyguardMediaController.getView());
+ }
+ requestChildrenUpdate();
+ return null;
+ });
}
private void initializeForegroundServiceSection(
@@ -1047,6 +1061,7 @@
Resources res = context.getResources();
mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
+ mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
mStackScrollAlgorithm.initView(context);
mAmbientState.reload(context);
mPaddingBetweenElements = Math.max(1,
@@ -3107,9 +3122,6 @@
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private boolean generateRemoveAnimation(ExpandableView child) {
- if (!child.wantsAddAndRemoveAnimations()) {
- return false;
- }
if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
mAddedHeadsUpChildren.remove(child);
return false;
@@ -3464,8 +3476,7 @@
@Override
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
- if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress && !isFullyHidden()
- && child.wantsAddAndRemoveAnimations()) {
+ if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress && !isFullyHidden()) {
// Generate Animations
mChildrenToAddAnimated.add(child);
if (fromMoreCard) {
@@ -3660,6 +3671,8 @@
ignoreChildren = false;
}
childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
+ } else if (child instanceof MediaHeaderView) {
+ childWasSwipedOut = true;
}
if (!childWasSwipedOut) {
Rect clipBounds = child.getClipBounds();
@@ -5088,8 +5101,10 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public int getFooterViewHeight() {
- return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
+ public int getFooterViewHeightWithPadding() {
+ return mFooterView == null ? 0 : mFooterView.getHeight()
+ + mPaddingBetweenElements
+ + mGapHeight;
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -6377,7 +6392,7 @@
@Override
public void onDragCancelled(View v) {
setSwipingInProgress(false);
- mFalsingManager.onNotificatonStopDismissing();
+ mFalsingManager.onNotificationStopDismissing();
}
/**
@@ -6477,7 +6492,7 @@
@Override
public void onBeginDrag(View v) {
- mFalsingManager.onNotificatonStartDismissing();
+ mFalsingManager.onNotificationStartDismissing();
setSwipingInProgress(true);
mAmbientState.onBeginDrag((ExpandableView) v);
updateContinuousShadowDrawing();
@@ -6636,7 +6651,9 @@
/* Only ever called as a consequence of a lockscreen expansion gesture. */
@Override
public boolean onDraggedDown(View startingChild, int dragLengthY) {
- if (mStatusBarState == StatusBarState.KEYGUARD && hasActiveNotifications()) {
+ boolean canDragDown = hasActiveNotifications()
+ || mKeyguardMediaController.getView().getVisibility() == VISIBLE;
+ if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) {
mLockscreenGestureLogger.write(
MetricsEvent.ACTION_LS_SHADE,
(int) (dragLengthY / mDisplayMetrics.density),
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 90548ba..063305e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -221,7 +221,7 @@
*/
private VerticalNavigationHandle mOrientationHandle;
private WindowManager.LayoutParams mOrientationParams;
- private int mStartingQuickSwitchRotation;
+ private int mStartingQuickSwitchRotation = -1;
private int mCurrentRotation;
private ViewTreeObserver.OnGlobalLayoutListener mOrientationHandleGlobalLayoutListener;
private UiEventLogger mUiEventLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 06484a2..dbff643 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -74,6 +74,7 @@
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
@@ -372,6 +373,11 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (isGesturalMode(mNavBarMode) && mImeVisible
+ && event.getAction() == MotionEvent.ACTION_DOWN) {
+ SysUiStatsLog.write(SysUiStatsLog.IME_TOUCH_REPORTED,
+ (int) event.getX(), (int) event.getY());
+ }
return shouldDeadZoneConsumeTouchEvents(event) || super.onInterceptTouchEvent(event);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index e720d82..0b664e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2397,8 +2397,8 @@
}
@Override
- protected int getClearAllHeight() {
- return mNotificationStackScroller.getFooterViewHeight();
+ protected int getClearAllHeightWithPadding() {
+ return mNotificationStackScroller.getFooterViewHeightWithPadding();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index a902e1b..caddc4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -537,9 +537,9 @@
// the animation only to the last notification, and then jump to the maximum panel height so
// clear all just fades in and the decelerating motion is towards the last notification.
final boolean clearAllExpandHack = expand &&
- shouldExpandToTopOfClearAll(getMaxPanelHeight() - getClearAllHeight());
+ shouldExpandToTopOfClearAll(getMaxPanelHeight() - getClearAllHeightWithPadding());
if (clearAllExpandHack) {
- target = getMaxPanelHeight() - getClearAllHeight();
+ target = getMaxPanelHeight() - getClearAllHeightWithPadding();
}
if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
notifyExpandingFinished();
@@ -1030,9 +1030,9 @@
protected abstract boolean isClearAllVisible();
/**
- * @return the height of the clear all button, in pixels
+ * @return the height of the clear all button, in pixels including padding
*/
- protected abstract int getClearAllHeight();
+ protected abstract int getClearAllHeightWithPadding();
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 3df1c11..6bc0565 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -72,9 +72,6 @@
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
-
- // Creating AudioRecordingDisclosureBar and just letting it run
- new AudioRecordingDisclosureBar(mContext);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
index b73aeb3..5143e42 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
@@ -46,6 +46,9 @@
private var state = TransitionViewState()
private var pivot = PointF()
private var animator: ValueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
+ private var currentHeight: Int = 0
+ private var currentWidth: Int = 0
+ var sizeChangedListener: ((Int, Int) -> Unit)? = null
init {
animator.apply {
@@ -67,7 +70,16 @@
progress = animator.animatedFraction,
pivot = pivot,
resultState = currentState)
- view.setState(currentState)
+ applyStateToLayout(currentState)
+ }
+
+ private fun applyStateToLayout(state: TransitionViewState) {
+ transitionLayout?.setState(state)
+ if (currentHeight != state.height || currentWidth != state.width) {
+ currentHeight = state.height
+ currentWidth = state.width
+ sizeChangedListener?.invoke(currentWidth, currentHeight)
+ }
}
/**
@@ -213,7 +225,7 @@
this.state = state.copy()
if (applyImmediately || transitionLayout == null) {
animator.cancel()
- transitionLayout?.setState(this.state)
+ applyStateToLayout(this.state)
currentState = state.copy(reusedState = currentState)
} else if (animated) {
animationStartState = currentState.copy()
@@ -221,7 +233,7 @@
animator.startDelay = delay
animator.start()
} else if (!animator.isRunning) {
- transitionLayout?.setState(this.state)
+ applyStateToLayout(this.state)
currentState = state.copy(reusedState = currentState)
}
// otherwise the desired state was updated and the animation will go to the new target
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 7b11452..8ba5b99 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -56,12 +56,14 @@
private static final boolean DEBUG = false;
+ // NOTE: All these constants came from InsetsController.
public static final int ANIMATION_DURATION_SHOW_MS = 275;
public static final int ANIMATION_DURATION_HIDE_MS = 340;
public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
private static final int DIRECTION_NONE = 0;
private static final int DIRECTION_SHOW = 1;
private static final int DIRECTION_HIDE = 2;
+ private static final int FLOATING_IME_BOTTOM_INSET = -80;
SystemWindows mSystemWindows;
final Handler mHandler;
@@ -271,8 +273,16 @@
}
// Set frame, but only if the new frame isn't empty -- this maintains continuity
final Rect newFrame = imeSource.getFrame();
- if (newFrame.height() != 0) {
+ mImeFrame.set(newFrame);
+ final boolean isFloating = newFrame.height() == 0;
+ if (isFloating) {
+ // This is likely a "floating" or "expanded" IME, so to get animations, just
+ // pretend the ime has some size just below the screen.
mImeFrame.set(newFrame);
+ final int floatingInset = (int) (
+ mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId).density()
+ * FLOATING_IME_BOTTOM_INSET);
+ mImeFrame.bottom -= floatingInset;
}
if (DEBUG) {
Slog.d(TAG, "Run startAnim show:" + show + " was:"
@@ -316,6 +326,8 @@
SurfaceControl.Transaction t = mTransactionPool.acquire();
float value = (float) animation.getAnimatedValue();
t.setPosition(mImeSourceControl.getLeash(), x, value);
+ final float alpha = isFloating ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
+ t.setAlpha(mImeSourceControl.getLeash(), alpha);
dispatchPositionChanged(mDisplayId, imeTop(value), t);
t.apply();
mTransactionPool.release(t);
@@ -327,6 +339,8 @@
public void onAnimationStart(Animator animation) {
SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setPosition(mImeSourceControl.getLeash(), x, startY);
+ final float alpha = isFloating ? (startY - hiddenY) / (shownY - hiddenY) : 1.f;
+ t.setAlpha(mImeSourceControl.getLeash(), alpha);
if (DEBUG) {
Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
+ imeTop(hiddenY) + "->" + imeTop(shownY)
@@ -351,6 +365,7 @@
SurfaceControl.Transaction t = mTransactionPool.acquire();
if (!mCancelled) {
t.setPosition(mImeSourceControl.getLeash(), x, endY);
+ t.setAlpha(mImeSourceControl.getLeash(), 1.f);
}
dispatchEndPositioning(mDisplayId, mCancelled, t);
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 9aee11e..008dc12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.MediaHeaderView
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.util.mockito.capture
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -94,7 +95,7 @@
private fun triggerVisibilityListener() {
keyguardMediaController.attach(mediaHeaderView)
- verify(mediaHost).visibleChangedListener = visibilityListener.capture()
+ verify(mediaHost).addVisibilityChangeListener(capture(visibilityListener))
visibilityListener.value.invoke(true)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index a297f32..b7f317b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -242,4 +242,15 @@
assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString(
com.android.internal.R.string.ext_media_seamless_action))
}
+
+ @Test
+ fun bindDeviceResumptionPlayer() {
+ player.attach(holder)
+ val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+ emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null,
+ resumption = true)
+ player.bind(state)
+ assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
+ assertThat(seamless.isEnabled()).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 618ee89..9fdd9ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -79,8 +79,8 @@
mManager.addListener(mListener);
mMediaData = new MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null,
- new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, KEY,
- false);
+ new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, false,
+ KEY, false);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 54520be..20a6da5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -1,6 +1,11 @@
package com.android.systemui.media
-import android.app.Notification
+import android.app.Notification.MediaStyle
+import android.app.PendingIntent
+import android.media.MediaDescription
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -8,6 +13,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.SbnBuilder
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -18,12 +26,14 @@
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
private const val KEY = "KEY"
private const val PACKAGE_NAME = "com.android.systemui"
+private const val APP_NAME = "SystemUI"
+private const val SESSION_ARTIST = "artist"
+private const val SESSION_TITLE = "title"
private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
private fun <T> anyObject(): T {
@@ -36,33 +46,47 @@
class MediaDataManagerTest : SysuiTestCase() {
@Mock lateinit var mediaControllerFactory: MediaControllerFactory
- @Mock lateinit var backgroundExecutor: Executor
- @Mock lateinit var foregroundExecutor: Executor
+ @Mock lateinit var controller: MediaController
+ lateinit var session: MediaSession
+ lateinit var metadataBuilder: MediaMetadata.Builder
+ lateinit var backgroundExecutor: FakeExecutor
+ lateinit var foregroundExecutor: FakeExecutor
@Mock lateinit var dumpManager: DumpManager
@Mock lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock lateinit var mediaTimeoutListener: MediaTimeoutListener
@Mock lateinit var mediaResumeListener: MediaResumeListener
+ @Mock lateinit var pendingIntent: PendingIntent
@JvmField @Rule val mockito = MockitoJUnit.rule()
lateinit var mediaDataManager: MediaDataManager
lateinit var mediaNotification: StatusBarNotification
@Before
fun setup() {
+ foregroundExecutor = FakeExecutor(FakeSystemClock())
+ backgroundExecutor = FakeExecutor(FakeSystemClock())
mediaDataManager = MediaDataManager(context, backgroundExecutor, foregroundExecutor,
mediaControllerFactory, broadcastDispatcher, dumpManager,
mediaTimeoutListener, mediaResumeListener, useMediaResumption = true,
useQsMediaPlayer = true)
- val sbn = mock(StatusBarNotification::class.java)
- val notification = mock(Notification::class.java)
- whenever(notification.hasMediaSession()).thenReturn(true)
- whenever(notification.notificationStyle).thenReturn(Notification.MediaStyle::class.java)
- whenever(sbn.notification).thenReturn(notification)
- whenever(sbn.packageName).thenReturn(PACKAGE_NAME)
- mediaNotification = sbn
+ session = MediaSession(context, "MediaDataManagerTestSession")
+ mediaNotification = SbnBuilder().run {
+ setPkg(PACKAGE_NAME)
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_pause)
+ it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+ }
+ build()
+ }
+ metadataBuilder = MediaMetadata.Builder().apply {
+ putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
+ putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
+ }
+ whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller)
}
@After
fun tearDown() {
+ session.release()
mediaDataManager.destroy()
}
@@ -82,7 +106,7 @@
@Test
fun testLoadsMetadataOnBackground() {
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
- verify(backgroundExecutor).execute(anyObject())
+ assertThat(backgroundExecutor.numPending()).isEqualTo(1)
}
@Test
@@ -123,4 +147,66 @@
verify(listener).onMediaDataRemoved(eq(KEY))
}
-}
\ No newline at end of file
+
+ @Test
+ fun testOnNotificationRemoved_withResumption() {
+ // GIVEN that the manager has a notification with a resume action
+ val listener = TestListener()
+ mediaDataManager.addListener(listener)
+ whenever(controller.metadata).thenReturn(metadataBuilder.build())
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ val data = listener.data!!
+ assertThat(data.resumption).isFalse()
+ mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
+ // WHEN the notification is removed
+ mediaDataManager.onNotificationRemoved(KEY)
+ // THEN the media data indicates that it is
+ assertThat(listener.data!!.resumption).isTrue()
+ }
+
+ @Test
+ fun testAddResumptionControls() {
+ val listener = TestListener()
+ mediaDataManager.addListener(listener)
+ // WHEN resumption controls are added`
+ val desc = MediaDescription.Builder().run {
+ setTitle(SESSION_TITLE)
+ build()
+ }
+ mediaDataManager.addResumptionControls(desc, Runnable {}, session.sessionToken, APP_NAME,
+ pendingIntent, PACKAGE_NAME)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ // THEN the media data indicates that it is for resumption
+ val data = listener.data!!
+ assertThat(data.resumption).isTrue()
+ assertThat(data.song).isEqualTo(SESSION_TITLE)
+ assertThat(data.app).isEqualTo(APP_NAME)
+ assertThat(data.actions).hasSize(1)
+ }
+
+ /**
+ * Simple implementation of [MediaDataManager.Listener] for the test.
+ *
+ * Giving up on trying to get a mock Listener and ArgumentCaptor to work.
+ */
+ private class TestListener : MediaDataManager.Listener {
+ var data: MediaData? = null
+ var key: String? = null
+ var oldKey: String? = null
+
+ override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
+ this.key = key
+ this.oldKey = oldKey
+ this.data = data
+ }
+
+ override fun onMediaDataRemoved(key: String) {
+ this.key = key
+ oldKey = null
+ data = null
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index c9e6f55..91c5ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -70,7 +70,7 @@
@Mock
private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
@Mock
- private lateinit var mediaViewManager: MediaViewManager
+ private lateinit var mediaCarouselController: MediaCarouselController
@Mock
private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Captor
@@ -82,13 +82,13 @@
@Before
fun setup() {
- `when`(mediaViewManager.mediaFrame).thenReturn(mediaFrame)
+ `when`(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
mediaHiearchyManager = MediaHierarchyManager(
context,
statusBarStateController,
keyguardStateController,
bypassController,
- mediaViewManager,
+ mediaCarouselController,
notificationLockscreenUserManager,
wakefulnessLifecycle)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
@@ -97,7 +97,7 @@
setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS)
`when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
// We'll use the viewmanager to verify a few calls below, let's reset this.
- clearInvocations(mediaViewManager)
+ clearInvocations(mediaCarouselController)
}
@@ -118,14 +118,14 @@
fun testBlockedWhenScreenTurningOff() {
// Let's set it onto QS:
mediaHiearchyManager.qsExpansion = 1.0f
- verify(mediaViewManager).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
+ verify(mediaCarouselController).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
val observer = wakefullnessObserver.value
assertNotNull("lifecycle observer wasn't registered", observer)
observer.onStartedGoingToSleep()
- clearInvocations(mediaViewManager)
+ clearInvocations(mediaCarouselController)
mediaHiearchyManager.qsExpansion = 0.0f
- verify(mediaViewManager, times(0)).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
+ verify(mediaCarouselController, times(0)).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
}
@@ -133,13 +133,13 @@
fun testAllowedWhenNotTurningOff() {
// Let's set it onto QS:
mediaHiearchyManager.qsExpansion = 1.0f
- verify(mediaViewManager).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
+ verify(mediaCarouselController).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
val observer = wakefullnessObserver.value
assertNotNull("lifecycle observer wasn't registered", observer)
- clearInvocations(mediaViewManager)
+ clearInvocations(mediaCarouselController)
mediaHiearchyManager.qsExpansion = 0.0f
- verify(mediaViewManager).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
+ verify(mediaCarouselController).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 22f50d0..e0d2681 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
@@ -40,6 +42,7 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.graphics.Color;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
@@ -79,6 +82,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Collections;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeyguardIndicationControllerTest extends SysuiTestCase {
@@ -154,7 +159,8 @@
mController = new KeyguardIndicationController(mContext, mWakeLockBuilder,
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
- mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats);
+ mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
+ mUserManager);
mController.setIndicationArea(mIndicationArea);
mController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
clearInvocations(mIBatteryStats);
@@ -242,6 +248,7 @@
@Test
public void disclosure_unmanaged() {
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
+ when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(false);
createController();
verify(mDisclosure).setVisibility(View.GONE);
@@ -249,7 +256,7 @@
}
@Test
- public void disclosure_managedNoOwnerName() {
+ public void disclosure_deviceOwner_noOwnerName() {
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
createController();
@@ -260,6 +267,19 @@
}
@Test
+ public void disclosure_orgOwnedDeviceWithManagedProfile_noOwnerName() {
+ when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true);
+ when(mUserManager.getProfiles(anyInt())).thenReturn(Collections.singletonList(
+ new UserInfo(10, /* name */ null, /* flags */ FLAG_MANAGED_PROFILE)));
+ when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(null);
+ createController();
+
+ verify(mDisclosure).setVisibility(View.VISIBLE);
+ verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
+ verifyNoMoreInteractions(mDisclosure);
+ }
+
+ @Test
public void disclosure_hiddenWhenDozing() {
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
@@ -292,7 +312,7 @@
}
@Test
- public void disclosure_managedOwnerName() {
+ public void disclosure_deviceOwner_withOwnerName() {
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
createController();
@@ -303,6 +323,19 @@
}
@Test
+ public void disclosure_orgOwnedDeviceWithManagedProfile_withOwnerName() {
+ when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true);
+ when(mUserManager.getProfiles(anyInt())).thenReturn(Collections.singletonList(
+ new UserInfo(10, /* name */ null, FLAG_MANAGED_PROFILE)));
+ when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(ORGANIZATION_NAME);
+ createController();
+
+ verify(mDisclosure).setVisibility(View.VISIBLE);
+ verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
+ verifyNoMoreInteractions(mDisclosure);
+ }
+
+ @Test
public void disclosure_updateOnTheFly() {
ArgumentCaptor<BroadcastReceiver> receiver = ArgumentCaptor.forClass(
BroadcastReceiver.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 06bad80..c979dc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.logging;
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_ALERTING;
-
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -200,7 +198,7 @@
assertEquals(TEST_UID, n.uid);
assertEquals(1, n.instanceId);
assertFalse(n.isGroupSummary);
- assertEquals(1 + BUCKET_ALERTING, n.section);
+ assertEquals(Notifications.Notification.SECTION_ALERTING, n.section);
}
@Test
@@ -217,7 +215,7 @@
assertEquals(TEST_UID, n.uid);
assertEquals(1, n.instanceId);
assertFalse(n.isGroupSummary);
- assertEquals(1 + BUCKET_ALERTING, n.section);
+ assertEquals(Notifications.Notification.SECTION_ALERTING, n.section);
}
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 c4bd1b2..b286f94 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
@@ -53,6 +53,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
@@ -133,6 +134,7 @@
@Mock private MetricsLogger mMetricsLogger;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
@Mock private KeyguardBypassController mKeyguardBypassController;
+ @Mock private KeyguardMediaController mKeyguardMediaController;
@Mock private ZenModeController mZenModeController;
@Mock private NotificationSectionsManager mNotificationSectionsManager;
@Mock private NotificationSection mNotificationSection;
@@ -209,6 +211,7 @@
mock(SysuiStatusBarStateController.class),
mHeadsUpManager,
mKeyguardBypassController,
+ mKeyguardMediaController,
new FalsingManagerFake(),
mLockscreenUserManager,
mock(NotificationGutsManager.class),
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3114a6a..2b9ce2f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1005,6 +1005,10 @@
|| disableDuration > 0) {
// Response is "empty" from an UI point of view, need to notify client.
notifyUnavailableToClient(sessionFinishedState, /* autofillableIds= */ null);
+ synchronized (mLock) {
+ mInlineSessionController.setInlineFillUiLocked(
+ InlineFillUi.emptyUi(mCurrentViewId));
+ }
}
if (requestLog != null) {
@@ -2979,8 +2983,8 @@
inlineSuggestionsRequest.get(), response, focusedId, filterText,
/*uiCallback*/this, /*onErrorCallback*/ () -> {
synchronized (mLock) {
- mInlineSessionController.hideInlineSuggestionsUiLocked(
- focusedId);
+ mInlineSessionController.setInlineFillUiLocked(
+ InlineFillUi.emptyUi(focusedId));
}
}, remoteRenderService);
return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
@@ -3166,12 +3170,15 @@
notifyUnavailableToClient(AutofillManager.STATE_FINISHED, autofillableIds);
removeSelf();
} else {
- if (sVerbose) {
- if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) {
+ if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) {
+ if (sVerbose) {
Slog.v(TAG, "keeping session " + id + " when service returned null and "
+ "augmented service is disabled for password fields. "
+ "AutofillableIds: " + autofillableIds);
- } else {
+ }
+ mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
+ } else {
+ if (sVerbose) {
Slog.v(TAG, "keeping session " + id + " when service returned null but "
+ "it can be augmented. AutofillableIds: " + autofillableIds);
}
@@ -3197,7 +3204,7 @@
// non-null response but without datasets (for example, just SaveInfo)
@GuardedBy("mLock")
private Runnable triggerAugmentedAutofillLocked(int flags) {
- // (TODO: b/141703197) Fix later by passing info to service.
+ // TODO: (b/141703197) Fix later by passing info to service.
if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) {
return null;
}
@@ -3242,7 +3249,7 @@
+ ComponentName.flattenToShortString(mComponentName) + " not whitelisted ");
}
logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(),
- mCurrentViewId, isWhitelisted, /*isInline*/null);
+ mCurrentViewId, isWhitelisted, /* isInline= */ null);
return null;
}
@@ -3284,6 +3291,10 @@
/*onErrorCallback=*/ () -> {
synchronized (mLock) {
cancelAugmentedAutofillLocked();
+
+ // Also cancel augmented in IME
+ mInlineSessionController.setInlineFillUiLocked(
+ InlineFillUi.emptyUi(mCurrentViewId));
}
}, mService.getRemoteInlineSuggestionRenderServiceLocked());
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 507e983..1c31166 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -33,6 +33,7 @@
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.IBinder;
+import android.os.UserHandle;
import android.service.autofill.BatchUpdates;
import android.service.autofill.CustomDescription;
import android.service.autofill.InternalOnClickAction;
@@ -196,7 +197,9 @@
}
intent.putExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY, true);
- PendingIntent p = PendingIntent.getActivity(this, 0, intent, 0);
+ PendingIntent p = PendingIntent.getActivityAsUser(
+ this, /* requestCode= */ 0, intent, /* flags= */ 0, /* options= */ null,
+ UserHandle.CURRENT);
if (sDebug) {
Slog.d(TAG, "startActivity add save UI restored with intent=" + intent);
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index dc35c77..f6c4918 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -318,7 +318,7 @@
private static final String SERIAL_ID_FILE = "serial_id";
- private static final String SKIP_USER_FACING_DATA = "backup_skip_user_facing_data";
+ private static final String SKIP_USER_FACING_PACKAGES = "backup_skip_user_facing_packages";
private static final String WALLPAPER_PACKAGE = "com.android.wallpaperbackup";
private final @UserIdInt int mUserId;
@@ -3557,7 +3557,7 @@
}
/**
- * We want to skip backup/restore of certain packages if 'backup_skip_user_facing_data' is
+ * We want to skip backup/restore of certain packages if 'backup_skip_user_facing_packages' is
* set to true in secure settings. See b/153940088 for details.
*
* TODO(b/154822946): Remove this logic in the next release.
@@ -3581,7 +3581,7 @@
@VisibleForTesting
public boolean shouldSkipUserFacingData() {
- return Settings.Secure.getInt(mContext.getContentResolver(), SKIP_USER_FACING_DATA,
+ return Settings.Secure.getInt(mContext.getContentResolver(), SKIP_USER_FACING_PACKAGES,
/* def */ 0) != 0;
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b46bebb..6e8eca3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -458,6 +458,12 @@
"(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/(?:data|media|obb|sandbox)/)([^/]+)(/.*)?");
+ /** Automotive device unlockes users before system boot complete and this requires special
+ * handling as vold reset can lead into race conditions. When this is set, all users unlocked
+ * in {@code UserManager} level are unlocked after vold reset.
+ */
+ private final boolean mIsAutomotive;
+
private VolumeInfo findVolumeByIdOrThrow(String id) {
synchronized (mLock) {
final VolumeInfo vol = mVolumes.get(id);
@@ -1082,14 +1088,12 @@
Slog.d(TAG, "Thinking about reset, mBootCompleted=" + mBootCompleted
+ ", mDaemonConnected=" + mDaemonConnected);
if (mBootCompleted && mDaemonConnected) {
- final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
+ final List<UserInfo> users = userManager.getUsers();
if (mIsFuseEnabled) {
mStorageSessionController.onReset(mVold, () -> {
- mHandler.removeMessages(H_RESET);
- mHandler.removeMessages(H_VOLUME_BROADCAST);
- mHandler.removeMessages(H_VOLUME_MOUNT);
- mHandler.removeMessages(H_VOLUME_UNMOUNT);
+ mHandler.removeCallbacksAndMessages(null);
});
} else {
killMediaProvider(users);
@@ -1097,7 +1101,9 @@
final int[] systemUnlockedUsers;
synchronized (mLock) {
- systemUnlockedUsers = mSystemUnlockedUsers;
+ // make copy as sorting can change order
+ systemUnlockedUsers = Arrays.copyOf(mSystemUnlockedUsers,
+ mSystemUnlockedUsers.length);
mDisks.clear();
mVolumes.clear();
@@ -1119,6 +1125,9 @@
mVold.onUserStarted(userId);
mStoraged.onUserStarted(userId);
}
+ if (mIsAutomotive) {
+ restoreAllUnlockedUsers(userManager, users, systemUnlockedUsers);
+ }
mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
mStorageManagerInternal.onReset(mVold);
} catch (Exception e) {
@@ -1127,6 +1136,29 @@
}
}
+ private void restoreAllUnlockedUsers(UserManager userManager, List<UserInfo> allUsers,
+ int[] systemUnlockedUsers) throws Exception {
+ Arrays.sort(systemUnlockedUsers);
+ UserManager.invalidateIsUserUnlockedCache();
+ for (UserInfo user : allUsers) {
+ int userId = user.id;
+ if (!userManager.isUserRunning(userId)) {
+ continue;
+ }
+ if (Arrays.binarySearch(systemUnlockedUsers, userId) >= 0) {
+ continue;
+ }
+ boolean unlockingOrUnlocked = userManager.isUserUnlockingOrUnlocked(userId);
+ if (!unlockingOrUnlocked) {
+ continue;
+ }
+ Slog.w(TAG, "UNLOCK_USER lost from vold reset, will retry, user:" + userId);
+ mVold.onUserStarted(userId);
+ mStoraged.onUserStarted(userId);
+ mHandler.obtainMessage(H_COMPLETE_UNLOCK_USER, userId).sendToTarget();
+ }
+ }
+
private void onUnlockUser(int userId) {
Slog.d(TAG, "onUnlockUser " + userId);
@@ -1157,6 +1189,15 @@
// Record user as started so newly mounted volumes kick off events
// correctly, then synthesize events for any already-mounted volumes.
synchronized (mLock) {
+ if (mIsAutomotive) {
+ for (int unlockedUser : mSystemUnlockedUsers) {
+ if (unlockedUser == userId) {
+ // This can happen as restoreAllUnlockedUsers can double post the message.
+ Log.i(TAG, "completeUnlockUser called for already unlocked user:" + userId);
+ return;
+ }
+ }
+ }
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) {
@@ -1822,6 +1863,9 @@
if (WATCHDOG_ENABLE) {
Watchdog.getInstance().addMonitor(this);
}
+
+ mIsAutomotive = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
}
/**
@@ -4477,6 +4521,7 @@
pw.println("Forced scoped storage app list: "
+ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
PROP_FORCED_SCOPED_STORAGE_WHITELIST));
+ pw.println("isAutomotive:" + mIsAutomotive);
}
synchronized (mObbMounts) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index f2c4e44..2d8d2c3 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -348,6 +348,17 @@
@GuardedBy("mUserIdToUserJourneyMap")
private final SparseArray<UserJourneySession> mUserIdToUserJourneyMap = new SparseArray<>();
+ /**
+ * Sets on {@link #setInitialConfig(boolean, int, boolean)}, which is called by
+ * {@code ActivityManager} when the system is started.
+ *
+ * <p>It's useful to ignore external operations (i.e., originated outside {@code system_server},
+ * like from {@code adb shell am switch-user})) that could happen before such call is made and
+ * the system is ready.
+ */
+ @GuardedBy("mLock")
+ private boolean mInitialized;
+
UserController(ActivityManagerService service) {
this(new Injector(service));
}
@@ -372,6 +383,7 @@
mUserSwitchUiEnabled = userSwitchUiEnabled;
mMaxRunningUsers = maxRunningUsers;
mDelayUserDataLocking = delayUserDataLocking;
+ mInitialized = true;
}
}
@@ -1587,6 +1599,11 @@
}
boolean userSwitchUiEnabled;
synchronized (mLock) {
+ if (!mInitialized) {
+ Slog.e(TAG, "Cannot switch to User #" + targetUserId
+ + ": UserController not ready yet");
+ return false;
+ }
mTargetUserId = targetUserId;
userSwitchUiEnabled = mUserSwitchUiEnabled;
}
@@ -2422,6 +2439,7 @@
pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking);
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
+ pw.println(" mInitialized:" + mInitialized);
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7e172a2..b5c173c 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3744,7 +3744,7 @@
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForAllPkgsInUid,
this, code, uidState.uid, true, null));
- } else {
+ } else if (uidState.pkgOps != null) {
final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
if (callbacks != null) {
for (int cbi = callbacks.size() - 1; cbi >= 0; cbi--) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3627227..bbc24ea 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -920,6 +920,8 @@
if (mHdmiManager != null) {
mHdmiManager.addHdmiControlStatusChangeListener(
mHdmiControlStatusChangeListenerCallback);
+ mHdmiManager.addHdmiCecVolumeControlFeatureListener(mContext.getMainExecutor(),
+ mMyHdmiCecVolumeControlFeatureListener);
}
mHdmiTvClient = mHdmiManager.getTvClient();
if (mHdmiTvClient != null) {
@@ -2248,6 +2250,7 @@
if (mHdmiManager != null) {
// mHdmiCecSink true => mHdmiPlaybackClient != null
if (mHdmiCecSink
+ && mHdmiCecVolumeControlEnabled
&& streamTypeAlias == AudioSystem.STREAM_MUSIC
// vol change on a full volume device
&& isFullVolumeDevice(device)) {
@@ -2320,7 +2323,8 @@
@GuardedBy("mHdmiClientLock")
private void maybeSendSystemAudioStatusCommand(boolean isMuteAdjust) {
if (mHdmiAudioSystemClient == null
- || !mHdmiSystemAudioSupported) {
+ || !mHdmiSystemAudioSupported
+ || !mHdmiCecVolumeControlEnabled) {
return;
}
@@ -2340,7 +2344,8 @@
|| mHdmiTvClient == null
|| oldVolume == newVolume
|| (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) != 0
- || !mHdmiSystemAudioSupported) {
+ || !mHdmiSystemAudioSupported
+ || !mHdmiCecVolumeControlEnabled) {
return;
}
final long token = Binder.clearCallingIdentity();
@@ -2940,16 +2945,18 @@
mVolumeController.postVolumeChanged(streamType, flags);
}
- // If Hdmi-CEC system audio mode is on and we are a TV panel, never show volume bar.
+ // Don't show volume UI when:
+ // - Hdmi-CEC system audio mode is on and we are a TV panel
+ // - CEC volume control enabled on a set-top box
private int updateFlagsForTvPlatform(int flags) {
synchronized (mHdmiClientLock) {
- if (mHdmiTvClient != null && mHdmiSystemAudioSupported) {
+ if ((mHdmiTvClient != null && mHdmiSystemAudioSupported && mHdmiCecVolumeControlEnabled)
+ || (mHdmiPlaybackClient != null && mHdmiCecVolumeControlEnabled)) {
flags &= ~AudioManager.FLAG_SHOW_UI;
}
}
return flags;
}
-
// UI update and Broadcast Intent
private void sendMasterMuteUpdate(boolean muted, int flags) {
mVolumeController.postMasterMuteChanged(updateFlagsForTvPlatform(flags));
@@ -7141,9 +7148,21 @@
}
};
+ private class MyHdmiCecVolumeControlFeatureListener
+ implements HdmiControlManager.HdmiCecVolumeControlFeatureListener {
+ public void onHdmiCecVolumeControlFeature(boolean enabled) {
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager == null) return;
+ mHdmiCecVolumeControlEnabled = enabled;
+ }
+ }
+ };
+
private final Object mHdmiClientLock = new Object();
// If HDMI-CEC system audio is supported
+ // Note that for CEC volume commands mHdmiCecVolumeControlEnabled will play a role on volume
+ // commands
private boolean mHdmiSystemAudioSupported = false;
// Set only when device is tv.
@GuardedBy("mHdmiClientLock")
@@ -7161,10 +7180,16 @@
// Set only when device is an audio system.
@GuardedBy("mHdmiClientLock")
private HdmiAudioSystemClient mHdmiAudioSystemClient;
+ // True when volume control over HDMI CEC is used when CEC is enabled (meaningless otherwise)
+ @GuardedBy("mHdmiClientLock")
+ private boolean mHdmiCecVolumeControlEnabled;
private MyHdmiControlStatusChangeListenerCallback mHdmiControlStatusChangeListenerCallback =
new MyHdmiControlStatusChangeListenerCallback();
+ private MyHdmiCecVolumeControlFeatureListener mMyHdmiCecVolumeControlFeatureListener =
+ new MyHdmiCecVolumeControlFeatureListener();
+
@Override
public int setHdmiSystemAudioSupported(boolean on) {
int device = AudioSystem.DEVICE_NONE;
@@ -7403,6 +7428,7 @@
pw.print(" mHdmiPlaybackClient="); pw.println(mHdmiPlaybackClient);
pw.print(" mHdmiTvClient="); pw.println(mHdmiTvClient);
pw.print(" mHdmiSystemAudioSupported="); pw.println(mHdmiSystemAudioSupported);
+ pw.print(" mHdmiCecVolumeControlEnabled="); pw.println(mHdmiCecVolumeControlEnabled);
pw.print(" mIsCallScreeningModeSupported="); pw.println(mIsCallScreeningModeSupported);
pw.print(" mic mute FromSwitch=" + mMicMuteFromSwitch
+ " FromRestrictions=" + mMicMuteFromRestrictions
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 0ac4f9e..f4a8667 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -213,6 +213,7 @@
mLocalDeviceAddresses = initLocalDeviceAddresses();
resetSelectRequestBuffer();
launchDeviceDiscovery();
+ startQueuedActions();
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 802a355..09fd33d 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -212,7 +212,7 @@
private static final int DIMENSION_KEY_SIZE_HARD_LIMIT = 800;
private static final int DIMENSION_KEY_SIZE_SOFT_LIMIT = 500;
private static final long APP_OPS_SAMPLING_INITIALIZATION_DELAY_MILLIS = 45000;
- private static final int APP_OPS_SIZE_ESTIMATE = 5000;
+ private static final int APP_OPS_SIZE_ESTIMATE = 2000;
private static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
/**
@@ -320,8 +320,7 @@
private StatsPullAtomCallbackImpl mStatsCallbackImpl;
- private final Object mAppOpsSamplingRateLock = new Object();
- @GuardedBy("mAppOpsSamplingRateLock")
+ @GuardedBy("mAttributedAppOpsLock")
private int mAppOpsSamplingRate = 0;
private final Object mDangerousAppOpsListLock = new Object();
@GuardedBy("mDangerousAppOpsListLock")
@@ -3084,7 +3083,7 @@
int pullDangerousPermissionStateLocked(int atomTag, List<StatsEvent> pulledData) {
final long token = Binder.clearCallingIdentity();
float samplingRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_PERMISSIONS,
- DANGEROUS_PERMISSION_STATE_SAMPLE_RATE, 0.02f);
+ DANGEROUS_PERMISSION_STATE_SAMPLE_RATE, 0.015f);
Set<Integer> reportedUids = new HashSet<>();
try {
PackageManager pm = mContext.getPackageManager();
@@ -3479,23 +3478,21 @@
HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
- synchronized (mAppOpsSamplingRateLock) {
- if (mAppOpsSamplingRate == 0) {
- mContext.getMainThreadHandler().postDelayed(new Runnable() {
- @Override
- public void run() {
- try {
- estimateAppOpsSamplingRate();
- } catch (Throwable e) {
- Slog.e(TAG, "AppOps sampling ratio estimation failed: ", e);
- synchronized (mAppOpsSamplingRateLock) {
- mAppOpsSamplingRate = min(mAppOpsSamplingRate, 10);
- }
+ if (mAppOpsSamplingRate == 0) {
+ mContext.getMainThreadHandler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ estimateAppOpsSamplingRate();
+ } catch (Throwable e) {
+ Slog.e(TAG, "AppOps sampling ratio estimation failed: ", e);
+ synchronized (mAttributedAppOpsLock) {
+ mAppOpsSamplingRate = min(mAppOpsSamplingRate, 10);
}
}
- }, APP_OPS_SAMPLING_INITIALIZATION_DELAY_MILLIS);
- mAppOpsSamplingRate = 100;
- }
+ }
+ }, APP_OPS_SAMPLING_INITIALIZATION_DELAY_MILLIS);
+ mAppOpsSamplingRate = 100;
}
List<AppOpEntry> opsList =
@@ -3503,9 +3500,7 @@
int newSamplingRate = sampleAppOps(pulledData, opsList, atomTag, mAppOpsSamplingRate);
- synchronized (mAppOpsSamplingRateLock) {
- mAppOpsSamplingRate = min(mAppOpsSamplingRate, newSamplingRate);
- }
+ mAppOpsSamplingRate = min(mAppOpsSamplingRate, newSamplingRate);
} catch (Throwable t) {
// TODO: catch exceptions at a more granular level
Slog.e(TAG, "Could not read appops", t);
@@ -3544,7 +3539,7 @@
}
int estimatedSamplingRate = (int) constrain(
appOpsTargetCollectionSize * 100 / estimatedSize, 0, 100);
- synchronized (mAppOpsSamplingRateLock) {
+ synchronized (mAttributedAppOpsLock) {
mAppOpsSamplingRate = min(mAppOpsSamplingRate, estimatedSamplingRate);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
new file mode 100644
index 0000000..b88573a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiCecLocalDeviceTv} class. */
+public class HdmiCecLocalDeviceTvTest {
+
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
+ private FakeNativeWrapper mNativeWrapper;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private int mTvPhysicalAddress;
+
+ @Mock
+ private IPowerManager mIPowerManagerMock;
+ @Mock
+ private IThermalService mIThermalServiceMock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Context context = InstrumentationRegistry.getTargetContext();
+ mMyLooper = mTestLooper.getLooper();
+ PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
+ mHdmiControlService =
+ new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ @Override
+ boolean isControlEnabled() {
+ return true;
+ }
+
+ @Override
+ boolean isTvDevice() {
+ return true;
+ }
+
+ @Override
+ void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
+
+ @Override
+ PowerManager getPowerManager() {
+ return powerManager;
+ }
+ };
+
+ mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
+ mHdmiCecLocalDeviceTv.init();
+ mHdmiControlService.setIoLooper(mMyLooper);
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mLocalDevices.add(mHdmiCecLocalDeviceTv);
+ mHdmiControlService.initPortInfo();
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTvPhysicalAddress = 0x0000;
+ mNativeWrapper.setPhysicalAddress(mTvPhysicalAddress);
+ mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
+ }
+
+ @Test
+ public void initialPowerStateIsStandby() {
+ assertThat(mHdmiCecLocalDeviceTv.getPowerStatus()).isEqualTo(
+ HdmiControlManager.POWER_STATUS_STANDBY);
+ }
+
+ @Test
+ public void onAddressAllocated_invokesDeviceDiscovery() {
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mHdmiCecLocalDeviceTv.onAddressAllocated(0, HdmiControlService.INITIATED_BY_BOOT_UP);
+
+ mTestLooper.dispatchAll();
+
+ // Check for for <Give Physical Address> being sent to available device (ADDR_PLAYBACK_1).
+ // This message is sent as part of the DeviceDiscoveryAction to available devices.
+ HdmiCecMessage givePhysicalAddress = HdmiCecMessageBuilder.buildGivePhysicalAddress(ADDR_TV,
+ ADDR_PLAYBACK_1);
+ assertThat(mNativeWrapper.getResultMessages()).contains(givePhysicalAddress);
+ }
+}
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 1a38a42..bc987a6 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -303,12 +303,6 @@
String message, boolean allowCarrierPrivilegeOnAnySub) {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
- PermissionManager permissionManager = (PermissionManager) context.getSystemService(
- Context.PERMISSION_SERVICE);
- if (permissionManager.checkDeviceIdentifierAccess(callingPackage, message, callingFeatureId,
- pid, uid) == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
// If the calling package has carrier privileges for specified sub, then allow access.
if (checkCarrierPrivilegeForSubId(context, subId)) return true;
@@ -319,6 +313,13 @@
return true;
}
+ PermissionManager permissionManager = (PermissionManager) context.getSystemService(
+ Context.PERMISSION_SERVICE);
+ if (permissionManager.checkDeviceIdentifierAccess(callingPackage, message, callingFeatureId,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+
return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
message);
}
@@ -433,16 +434,6 @@
public static boolean checkReadPhoneNumber(
Context context, int subId, int pid, int uid,
String callingPackage, @Nullable String callingFeatureId, String message) {
- // Default SMS app can always read it.
- AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- if (appOps.noteOp(AppOpsManager.OPSTR_WRITE_SMS, uid, callingPackage, callingFeatureId,
- null) == AppOpsManager.MODE_ALLOWED) {
- return true;
- }
-
- // NOTE(b/73308711): If an app has one of the following AppOps bits explicitly revoked, they
- // will be denied access, even if they have another permission and AppOps bit if needed.
-
// First, check if the SDK version is below R
boolean preR = false;
try {
@@ -477,21 +468,29 @@
}
}
+ // Default SMS app can always read it.
+ AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ if (appOps.noteOp(AppOpsManager.OPSTR_WRITE_SMS, uid, callingPackage, callingFeatureId,
+ null) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
// Can be read with READ_SMS too.
try {
context.enforcePermission(android.Manifest.permission.READ_SMS, pid, uid, message);
- return appOps.noteOp(AppOpsManager.OPSTR_READ_SMS, uid, callingPackage,
- callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
-
+ if (appOps.noteOp(AppOpsManager.OPSTR_READ_SMS, uid, callingPackage,
+ callingFeatureId, null) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
} catch (SecurityException readSmsSecurityException) {
}
// Can be read with READ_PHONE_NUMBERS too.
try {
context.enforcePermission(android.Manifest.permission.READ_PHONE_NUMBERS, pid, uid,
message);
- return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_NUMBERS, uid, callingPackage,
- callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
-
+ if (appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_NUMBERS, uid, callingPackage,
+ callingFeatureId, null) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
} catch (SecurityException readPhoneNumberSecurityException) {
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index d62cd0a..11667c8 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -305,11 +305,14 @@
}
/**
- * Returns the ICC ID if the calling app has been granted the READ_PRIVILEGED_PHONE_STATE
- * permission, has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or
- * is a device owner or profile owner that has been granted the READ_PHONE_STATE permission.
- * The profile owner is an app that owns a managed profile on the device; for more details see
- * <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile
+ * Returns the ICC ID.
+ *
+ * Starting with API level 30, returns the ICC ID if the calling app has been granted the
+ * READ_PRIVILEGED_PHONE_STATE permission, has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}), or is a device owner or profile owner that
+ * has been granted the READ_PHONE_STATE permission. The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile
* owner access is deprecated and will be removed in a future release.
*
* @return the ICC ID, or an empty string if one of these requirements is not met
@@ -449,8 +452,22 @@
}
/**
- * @return the number of this subscription if the calling app has been granted the
- * READ_PHONE_NUMBERS permission, or an empty string otherwise
+ * Returns the number of this subscription.
+ *
+ * Starting with API level 30, returns the number of this subscription if the calling app meets
+ * one of the following requirements:
+ * <ul>
+ * <li>If the calling app's target SDK is API level 29 or lower and the app has been granted
+ * the READ_PHONE_STATE permission.
+ * <li>If the calling app has been granted any of READ_PRIVILEGED_PHONE_STATE,
+ * READ_PHONE_NUMBERS, or READ_SMS.
+ * <li>If the calling app has carrier privileges (see {@link
+ * TelephonyManager#hasCarrierPrivileges}).
+ * <li>If the calling app is the default SMS role holder.
+ * </ul>
+ *
+ * @return the number of this subscription, or an empty string if one of these requirements is
+ * not met
*/
public String getNumber() {
return mNumber;
@@ -670,12 +687,15 @@
}
/**
- * Returns the card string if the calling app has been granted the READ_PRIVILEGED_PHONE_STATE
- * permission, has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or
- * is a device owner or profile owner on an organization owned device that has been granted the
- * READ_PHONE_STATE permission. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>.
+ * Returns the card string of the SIM card which contains the subscription.
+ *
+ * Starting with API level 30, returns the card string if the calling app has been granted the
+ * READ_PRIVILEGED_PHONE_STATE permission, has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}), or is a device owner or profile owner that
+ * has been granted the READ_PHONE_STATE permission. The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile
+ * owner access is deprecated and will be removed in a future release.
*
* @return the card string of the SIM card which contains the subscription or an empty string
* if these requirements are not met. The card string is the ICCID for UICCs or the EID for
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
index 4a0ca66..2df0024 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
@@ -153,7 +153,14 @@
public void writeToSession(BlobStoreManager.Session session,
long offsetBytes, long lengthBytes) throws Exception {
try (FileInputStream in = new FileInputStream(mFile)) {
- Utils.writeToSession(session, in, offsetBytes, lengthBytes);
+ Utils.writeToSession(session, in, offsetBytes, lengthBytes, lengthBytes);
+ }
+ }
+
+ public void writeToSession(BlobStoreManager.Session session,
+ long offsetBytes, long lengthBytes, long allocateBytes) throws Exception {
+ try (FileInputStream in = new FileInputStream(mFile)) {
+ Utils.writeToSession(session, in, offsetBytes, lengthBytes, allocateBytes);
}
}
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
index b9bd661..ec85995 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
@@ -59,15 +59,15 @@
public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input,
long lengthBytes) throws IOException {
try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) {
- writeToSession(session, in, 0, lengthBytes);
+ writeToSession(session, in, 0, lengthBytes, lengthBytes);
}
}
public static void writeToSession(BlobStoreManager.Session session, FileInputStream in,
- long offsetBytes, long lengthBytes) throws IOException {
+ long offsetBytes, long lengthBytes, long allocateBytes) throws IOException {
in.getChannel().position(offsetBytes);
try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
- session.openWrite(offsetBytes, lengthBytes))) {
+ session.openWrite(offsetBytes, allocateBytes))) {
copy(in, out, lengthBytes);
}
}
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index b1e2487..e3b6db0 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -21,7 +21,6 @@
name: "stats-log-api-gen",
srcs: [
"Collation.cpp",
- "atoms_info_writer.cpp",
"java_writer.cpp",
"java_writer_q.cpp",
"main.cpp",
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 958e94e..a230de4 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -52,9 +52,7 @@
defaultState(that.defaultState),
triggerStateReset(that.triggerStateReset),
nested(that.nested),
- uidField(that.uidField),
- whitelisted(that.whitelisted),
- truncateTimestamp(that.truncateTimestamp) {
+ uidField(that.uidField) {
}
AtomDecl::AtomDecl(int c, const string& n, const string& m) : code(c), name(n), message(m) {
@@ -520,13 +518,6 @@
shared_ptr<AtomDecl> atomDecl =
make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name());
- if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) {
- atomDecl->whitelisted = true;
- if (dbg) {
- printf("%s is whitelisted\n", atomField->name().c_str());
- }
- }
-
if (atomDecl->code < PULL_ATOM_START_ID &&
atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
addAnnotationToAtomDecl(atomDecl.get(), ATOM_ID_FIELD_NUMBER,
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 043f8b1..10b34ec 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -164,10 +164,6 @@
int uidField = 0;
- bool whitelisted = false;
-
- bool truncateTimestamp = false;
-
AtomDecl();
AtomDecl(const AtomDecl& that);
AtomDecl(int code, const string& name, const string& message);
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
deleted file mode 100644
index 292cb21..0000000
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ /dev/null
@@ -1,91 +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.
- */
-
-#include "atoms_info_writer.h"
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "utils.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-static void write_atoms_info_header_body(FILE* out) {
- fprintf(out, "struct AtomsInfo {\n");
- fprintf(out, " const static std::set<int> kWhitelistedAtoms;\n");
- fprintf(out, "};\n");
-}
-
-static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
-
- fprintf(out, "const std::set<int> AtomsInfo::kWhitelistedAtoms = {\n");
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- if ((*atomIt)->whitelisted) {
- const string constant = make_constant_name((*atomIt)->name);
- fprintf(out, " %d, // %s\n", (*atomIt)->code, constant.c_str());
- }
- }
-
- fprintf(out, "};\n");
- fprintf(out, "\n");
-
-}
-
-int write_atoms_info_header(FILE* out, const string& namespaceStr) {
- // Print prelude
- fprintf(out, "// This file is autogenerated\n");
- fprintf(out, "\n");
- fprintf(out, "#pragma once\n");
- fprintf(out, "\n");
- fprintf(out, "#include <vector>\n");
- fprintf(out, "#include <map>\n");
- fprintf(out, "#include <set>\n");
- fprintf(out, "\n");
-
- write_namespace(out, namespaceStr);
-
- write_atoms_info_header_body(out);
-
- fprintf(out, "\n");
- write_closing_namespace(out, namespaceStr);
-
- return 0;
-}
-
-int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr,
- const string& importHeader) {
- // Print prelude
- fprintf(out, "// This file is autogenerated\n");
- fprintf(out, "\n");
- fprintf(out, "#include <%s>\n", importHeader.c_str());
- fprintf(out, "\n");
-
- write_namespace(out, namespaceStr);
-
- write_atoms_info_cpp_body(out, atoms);
-
- // Print footer
- fprintf(out, "\n");
- write_closing_namespace(out, namespaceStr);
-
- return 0;
-}
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/atoms_info_writer.h b/tools/stats_log_api_gen/atoms_info_writer.h
deleted file mode 100644
index 09a4303..0000000
--- a/tools/stats_log_api_gen/atoms_info_writer.h
+++ /dev/null
@@ -1,35 +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.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <string.h>
-
-#include "Collation.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using namespace std;
-
-int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr,
- const string& importHeader);
-
-int write_atoms_info_header(FILE* out, const string& namespaceStr);
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 136933b..b888ce9 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -9,7 +9,6 @@
#include <vector>
#include "Collation.h"
-#include "atoms_info_writer.h"
#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
#include "java_writer.h"
#include "java_writer_q.h"
@@ -30,12 +29,6 @@
fprintf(stderr, "OPTIONS\n");
fprintf(stderr, " --cpp FILENAME the header file to output for write helpers\n");
fprintf(stderr, " --header FILENAME the cpp file to output for write helpers\n");
- fprintf(stderr,
- " --atomsInfoCpp FILENAME the header file to output for "
- "statsd metadata\n");
- fprintf(stderr,
- " --atomsInfoHeader FILENAME the cpp file to output for statsd "
- "metadata\n");
fprintf(stderr, " --help this message\n");
fprintf(stderr, " --java FILENAME the java file to output\n");
fprintf(stderr, " --module NAME optional, module name to generate outputs for\n");
@@ -49,10 +42,6 @@
" --importHeader NAME required for cpp/jni to say which header to "
"import "
"for write helpers\n");
- fprintf(stderr,
- " --atomsInfoImportHeader NAME required for cpp to say which "
- "header to import "
- "for statsd metadata\n");
fprintf(stderr, " --javaPackage PACKAGE the package for the java file.\n");
fprintf(stderr, " required for java with module\n");
fprintf(stderr, " --javaClass CLASS the class name of the java class.\n");
@@ -74,15 +63,12 @@
string cppFilename;
string headerFilename;
string javaFilename;
- string atomsInfoCppFilename;
- string atomsInfoHeaderFilename;
string javaPackage;
string javaClass;
string moduleName = DEFAULT_MODULE_NAME;
string cppNamespace = DEFAULT_CPP_NAMESPACE;
string cppHeaderImport = DEFAULT_CPP_HEADER_IMPORT;
- string atomsInfoCppHeaderImport = DEFAULT_ATOMS_INFO_CPP_HEADER_IMPORT;
bool supportQ = false;
bool supportWorkSource = false;
bool compileQ = false;
@@ -148,27 +134,6 @@
return 1;
}
javaClass = argv[index];
- } else if (0 == strcmp("--atomsInfoHeader", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- atomsInfoHeaderFilename = argv[index];
- } else if (0 == strcmp("--atomsInfoCpp", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- atomsInfoCppFilename = argv[index];
- } else if (0 == strcmp("--atomsInfoImportHeader", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- atomsInfoCppHeaderImport = argv[index];
} else if (0 == strcmp("--supportQ", argv[index])) {
supportQ = true;
} else if (0 == strcmp("--worksource", argv[index])) {
@@ -180,8 +145,7 @@
index++;
}
- if (cppFilename.size() == 0 && headerFilename.size() == 0 && javaFilename.size() == 0 &&
- atomsInfoHeaderFilename.size() == 0 && atomsInfoCppFilename.size() == 0) {
+ if (cppFilename.size() == 0 && headerFilename.size() == 0 && javaFilename.size() == 0) {
print_usage();
return 1;
}
@@ -210,29 +174,6 @@
collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
&attributionSignature);
- // Write the atoms info .cpp file
- if (atomsInfoCppFilename.size() != 0) {
- FILE* out = fopen(atomsInfoCppFilename.c_str(), "w");
- if (out == NULL) {
- fprintf(stderr, "Unable to open file for write: %s\n", atomsInfoCppFilename.c_str());
- return 1;
- }
- errorCount = android::stats_log_api_gen::write_atoms_info_cpp(out, atoms, cppNamespace,
- atomsInfoCppHeaderImport);
- fclose(out);
- }
-
- // Write the atoms info .h file
- if (atomsInfoHeaderFilename.size() != 0) {
- FILE* out = fopen(atomsInfoHeaderFilename.c_str(), "w");
- if (out == NULL) {
- fprintf(stderr, "Unable to open file for write: %s\n", atomsInfoHeaderFilename.c_str());
- return 1;
- }
- errorCount = android::stats_log_api_gen::write_atoms_info_header(out, cppNamespace);
- fclose(out);
- }
-
// Write the .cpp file
if (cppFilename.size() != 0) {
FILE* out = fopen(cppFilename.c_str(), "w");
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index d22acc6..aaa488e 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -187,24 +187,6 @@
optional int32 state = 3 [(android.os.statsd.state_field_option).exclusive_state = true];
}
-message WhitelistedAtom {
- optional int32 field = 1;
-}
-
-message NonWhitelistedAtom {
- optional int32 field = 1;
-}
-
-message ListedAtoms {
- oneof event {
- // Atoms can be whitelisted i.e. they can be triggered by any source
- WhitelistedAtom whitelisted_atom = 1 [(android.os.statsd.allow_from_any_uid) = true];
- // Atoms are not whitelisted by default, so they can only be triggered
- // by whitelisted sources
- NonWhitelistedAtom non_whitelisted_atom = 2;
- }
-}
-
message ModuleOneAtom {
optional int32 field = 1 [(android.os.statsd.is_uid) = true];
}
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 1504752..dbae588 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -225,25 +225,6 @@
EXPECT_TRUE(errorCount > 0);
}
-TEST(CollationTest, PassOnWhitelistedAtom) {
- Atoms atoms;
- int errorCount = collate_atoms(ListedAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
- EXPECT_EQ(errorCount, 0);
- EXPECT_EQ(atoms.decls.size(), 2ul);
-}
-
-TEST(CollationTest, RecogniseWhitelistedAtom) {
- Atoms atoms;
- collate_atoms(ListedAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
- for (const auto& atomDecl : atoms.decls) {
- if (atomDecl->code == 1) {
- EXPECT_TRUE(atomDecl->whitelisted);
- } else {
- EXPECT_FALSE(atomDecl->whitelisted);
- }
- }
-}
-
TEST(CollationTest, PassOnLogFromModuleAtom) {
Atoms atoms;
int errorCount = collate_atoms(ModuleAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h
index 7d6d08e..73e0cb8 100644
--- a/tools/stats_log_api_gen/utils.h
+++ b/tools/stats_log_api_gen/utils.h
@@ -32,7 +32,6 @@
const string DEFAULT_CPP_NAMESPACE = "android,util";
const string DEFAULT_CPP_HEADER_IMPORT = "statslog.h";
-const string DEFAULT_ATOMS_INFO_CPP_HEADER_IMPORT = "atoms_info.h";
const int JAVA_MODULE_REQUIRES_FLOAT = 0x01;
const int JAVA_MODULE_REQUIRES_ATTRIBUTION = 0x02;