Merge "Fix bug in statsd_test" into rvc-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index 51e030b..e8afa1d 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -63,6 +63,60 @@
"--hide-annotation android.annotation.Hide " +
"--hide InternalClasses " // com.android.* classes are okay in this interface
+// Defaults for mainline module provided java_sdk_library instances.
+java_defaults {
+ name: "framework-module-defaults",
+
+ // Additional annotations used for compiling both the implementation and the
+ // stubs libraries.
+ libs: ["framework-annotations-lib"],
+
+ // Enable api lint. This will eventually become the default for java_sdk_library
+ // but it cannot yet be turned on because some usages have not been cleaned up.
+ // TODO(b/156126315) - Remove when no longer needed.
+ api_lint: {
+ enabled: true,
+ },
+
+ // The API scope specific properties.
+ public: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+ system: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+ module_lib: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+
+ // The stub libraries must be visible to frameworks/base so they can be combined
+ // into API specific libraries.
+ stubs_library_visibility: [
+ "//frameworks/base", // Framework
+ ],
+
+ // Set the visibility of the modules creating the stubs source.
+ stubs_source_visibility: [
+ // Ignore any visibility rules specified on the java_sdk_library when
+ // setting the visibility of the stubs source modules.
+ "//visibility:override",
+
+ // Currently, the stub source is not required for anything other than building
+ // the stubs library so is private to avoid misuse.
+ "//visibility:private",
+ ],
+
+ // Collates API usages from each module for further analysis.
+ plugins: ["java_api_finder"],
+
+ // Mainline modules should only rely on 'module_lib' APIs provided by other modules
+ // and the non updatable parts of the platform.
+ sdk_version: "module_current",
+}
+
stubs_defaults {
name: "framework-module-stubs-defaults-publicapi",
args: mainline_framework_stubs_args,
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 46d449a..372ec98 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -79,6 +79,12 @@
private static final int STANDBY_BUCKET_UNKNOWN = -1;
+ /**
+ * The bucket beyond which apps are considered idle. Any apps in this bucket or lower are
+ * considered idle while those in higher buckets are not considered idle.
+ */
+ static final int IDLE_BUCKET_CUTOFF = STANDBY_BUCKET_RARE;
+
@VisibleForTesting
static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
private static final String TAG_PACKAGES = "packages";
@@ -350,7 +356,7 @@
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
- return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE;
+ return appUsageHistory.currentBucket >= IDLE_BUCKET_CUTOFF;
}
public AppUsageHistory getAppUsageHistory(String packageName, int userId,
@@ -487,7 +493,7 @@
final int newBucket;
final int reason;
if (idle) {
- newBucket = STANDBY_BUCKET_RARE;
+ newBucket = IDLE_BUCKET_CUTOFF;
reason = REASON_MAIN_FORCED_BY_USER;
} else {
newBucket = STANDBY_BUCKET_ACTIVE;
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 980372d..2834ab1 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -54,6 +54,7 @@
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -92,6 +93,7 @@
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Slog;
@@ -227,6 +229,13 @@
@GuardedBy("mActiveAdminApps")
private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
+ /**
+ * Set of system apps that are headless (don't have any declared activities, enabled or
+ * disabled). Presence in this map indicates that the app is a headless system app.
+ */
+ @GuardedBy("mAppIdleLock")
+ private final ArrayMap<String, Boolean> mHeadlessSystemApps = new ArrayMap<>();
+
private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
// Messages for the handler
@@ -667,20 +676,22 @@
return;
}
}
- final boolean isSpecial = isAppSpecial(packageName,
+ final int minBucket = getAppMinBucket(packageName,
UserHandle.getAppId(uid),
userId);
if (DEBUG) {
- Slog.d(TAG, " Checking idle state for " + packageName + " special=" +
- isSpecial);
+ Slog.d(TAG, " Checking idle state for " + packageName
+ + " minBucket=" + minBucket);
}
- if (isSpecial) {
+ if (minBucket <= STANDBY_BUCKET_ACTIVE) {
+ // No extra processing needed for ACTIVE or higher since apps can't drop into lower
+ // buckets.
synchronized (mAppIdleLock) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
+ minBucket, REASON_MAIN_DEFAULT);
}
maybeInformListeners(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT, false);
+ minBucket, REASON_MAIN_DEFAULT, false);
} else {
synchronized (mAppIdleLock) {
final AppIdleHistory.AppUsageHistory app =
@@ -761,6 +772,14 @@
Slog.d(TAG, "Bringing up from RESTRICTED to RARE due to off switch");
}
}
+ if (newBucket > minBucket) {
+ newBucket = minBucket;
+ // Leave the reason alone.
+ if (DEBUG) {
+ Slog.d(TAG, "Bringing up from " + newBucket + " to " + minBucket
+ + " due to min bucketing");
+ }
+ }
if (DEBUG) {
Slog.d(TAG, " Old bucket=" + oldBucket
+ ", newBucket=" + newBucket);
@@ -1027,20 +1046,35 @@
return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
}
- private boolean isAppSpecial(String packageName, int appId, int userId) {
- if (packageName == null) return false;
+ @StandbyBuckets
+ private int getAppMinBucket(String packageName, int userId) {
+ try {
+ final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+ return getAppMinBucket(packageName, UserHandle.getAppId(uid), userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Not a valid package for this user, nothing to do
+ return STANDBY_BUCKET_NEVER;
+ }
+ }
+
+ /**
+ * Return the lowest bucket this app should ever enter.
+ */
+ @StandbyBuckets
+ private int getAppMinBucket(String packageName, int appId, int userId) {
+ if (packageName == null) return STANDBY_BUCKET_NEVER;
// If not enabled at all, of course nobody is ever idle.
if (!mAppIdleEnabled) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
if (appId < Process.FIRST_APPLICATION_UID) {
// System uids never go idle.
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
if (packageName.equals("android")) {
// Nor does the framework (which should be redundant with the above, but for MR1 we will
// retain this for safety).
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
if (mSystemServicesReady) {
try {
@@ -1048,42 +1082,51 @@
// for idle mode, because app idle (aka app standby) is really not as big an issue
// for controlling who participates vs. doze mode.
if (mInjector.isNonIdleWhitelisted(packageName)) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
if (isActiveDeviceAdmin(packageName, userId)) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
if (isActiveNetworkScorer(packageName)) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
if (mAppWidgetManager != null
&& mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) {
- return true;
+ // TODO: consider lowering to ACTIVE
+ return STANDBY_BUCKET_EXEMPTED;
}
if (isDeviceProvisioningPackage(packageName)) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
}
// Check this last, as it can be the most expensive check
if (isCarrierApp(packageName)) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
- return false;
+ if (isHeadlessSystemApp(packageName)) {
+ return STANDBY_BUCKET_ACTIVE;
+ }
+
+ return STANDBY_BUCKET_NEVER;
+ }
+
+ private boolean isHeadlessSystemApp(String packageName) {
+ return mHeadlessSystemApps.containsKey(packageName);
}
@Override
public boolean isAppIdleFiltered(String packageName, int appId, int userId,
long elapsedRealtime) {
- if (isAppSpecial(packageName, appId, userId)) {
+ if (getAppMinBucket(packageName, appId, userId) < AppIdleHistory.IDLE_BUCKET_CUTOFF) {
return false;
} else {
synchronized (mAppIdleLock) {
@@ -1423,6 +1466,8 @@
}
}
+ // Make sure we don't put the app in a lower bucket than it's supposed to be in.
+ newBucket = Math.min(newBucket, getAppMinBucket(packageName, userId));
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
reason, resetTimeout);
}
@@ -1617,14 +1662,16 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
+ final String pkgName = intent.getData().getSchemeSpecificPart();
+ final int userId = getSendingUserId();
if (Intent.ACTION_PACKAGE_ADDED.equals(action)
|| Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
clearCarrierPrivilegedApps();
+ // ACTION_PACKAGE_ADDED is called even for system app downgrades.
+ evaluateSystemAppException(pkgName, userId);
}
if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
Intent.ACTION_PACKAGE_ADDED.equals(action))) {
- final String pkgName = intent.getData().getSchemeSpecificPart();
- final int userId = getSendingUserId();
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
maybeUnrestrictBuggyApp(pkgName, userId);
} else {
@@ -1634,6 +1681,34 @@
}
}
+ private void evaluateSystemAppException(String packageName, int userId) {
+ if (!mSystemServicesReady) {
+ // The app will be evaluated in initializeDefaultsForSystemApps() when possible.
+ return;
+ }
+ try {
+ PackageInfo pi = mPackageManager.getPackageInfoAsUser(packageName,
+ PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS,
+ userId);
+ evaluateSystemAppException(pi);
+ } catch (PackageManager.NameNotFoundException e) {
+ mHeadlessSystemApps.remove(packageName);
+ }
+ }
+
+ private void evaluateSystemAppException(@Nullable PackageInfo pkgInfo) {
+ if (pkgInfo.applicationInfo != null && pkgInfo.applicationInfo.isSystemApp()) {
+ synchronized (mAppIdleLock) {
+ if (pkgInfo.activities == null || pkgInfo.activities.length == 0) {
+ // Headless system app.
+ mHeadlessSystemApps.put(pkgInfo.packageName, true);
+ } else {
+ mHeadlessSystemApps.remove(pkgInfo.packageName);
+ }
+ }
+ }
+ }
+
@Override
public void initializeDefaultsForSystemApps(int userId) {
if (!mSystemServicesReady) {
@@ -1645,7 +1720,7 @@
+ "appIdleEnabled=" + mAppIdleEnabled);
final long elapsedRealtime = mInjector.elapsedRealtime();
List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS,
+ PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS,
userId);
final int packageCount = packages.size();
synchronized (mAppIdleLock) {
@@ -1658,6 +1733,8 @@
mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
+
+ evaluateSystemAppException(pi);
}
}
// Immediately persist defaults to disk
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index c3adf60..c1011ec 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.media.MediaCodec.CryptoInfo;
+import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -467,7 +468,24 @@
public @interface SampleFlags {}
/** Indicates that the sample holds a synchronization sample. */
public static final int SAMPLE_FLAG_KEY_FRAME = MediaCodec.BUFFER_FLAG_KEY_FRAME;
- /** Indicates that the sample has supplemental data. */
+ /**
+ * Indicates that the sample has supplemental data.
+ *
+ * <p>Samples will not have this flag set unless the {@code
+ * "android.media.mediaparser.includeSupplementalData"} parameter is set to {@code true} via
+ * {@link #setParameter}.
+ *
+ * <p>Samples with supplemental data have the following sample data format:
+ *
+ * <ul>
+ * <li>If the {@code "android.media.mediaparser.inBandCryptoInfo"} parameter is set, all
+ * encryption information.
+ * <li>(4 bytes) {@code sample_data_size}: The size of the actual sample data, not including
+ * supplemental data or encryption information.
+ * <li>({@code sample_data_size} bytes): The media sample data.
+ * <li>(remaining bytes) The supplemental data.
+ * </ul>
+ */
public static final int SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA = 1 << 28;
/** Indicates that the sample is known to contain the last media sample of the stream. */
public static final int SAMPLE_FLAG_LAST_SAMPLE = 1 << 29;
@@ -578,7 +596,9 @@
PARAMETER_TS_IGNORE_AVC_STREAM,
PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM,
PARAMETER_TS_DETECT_ACCESS_UNITS,
- PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS
+ PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS,
+ PARAMETER_IN_BAND_CRYPTO_INFO,
+ PARAMETER_INCLUDE_SUPPLEMENTAL_DATA
})
public @interface ParameterName {}
@@ -740,6 +760,16 @@
public static final String PARAMETER_IN_BAND_CRYPTO_INFO =
"android.media.mediaparser.inBandCryptoInfo";
+ /**
+ * Sets whether supplemental data should be included as part of the sample data. {@code boolean}
+ * expected. Default value is {@code false}. See {@link #SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA} for
+ * information about the sample data format.
+ *
+ * @hide
+ */
+ public static final String PARAMETER_INCLUDE_SUPPLEMENTAL_DATA =
+ "android.media.mediaparser.includeSupplementalData";
+
// Private constants.
private static final String TAG = "MediaParser";
@@ -899,6 +929,7 @@
private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
@Nullable private final Constructor<DrmInitData.SchemeInitData> mSchemeInitDataConstructor;
private boolean mInBandCryptoInfo;
+ private boolean mIncludeSupplementalData;
private String mParserName;
private Extractor mExtractor;
private ExtractorInput mExtractorInput;
@@ -949,6 +980,9 @@
if (PARAMETER_IN_BAND_CRYPTO_INFO.equals(parameterName)) {
mInBandCryptoInfo = (boolean) value;
}
+ if (PARAMETER_INCLUDE_SUPPLEMENTAL_DATA.equals(parameterName)) {
+ mIncludeSupplementalData = (boolean) value;
+ }
mParserParameters.put(parameterName, value);
return this;
}
@@ -1099,6 +1133,9 @@
// Private methods.
private MediaParser(OutputConsumer outputConsumer, boolean sniff, String... parserNamesPool) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ throw new UnsupportedOperationException("Android version must be R or greater.");
+ }
mParserParameters = new HashMap<>();
mOutputConsumer = outputConsumer;
mParserNamesPool = parserNamesPool;
@@ -1330,6 +1367,7 @@
private int mEncryptionVectorSize;
private boolean mHasSubsampleEncryptionData;
private CryptoInfo.Pattern mEncryptionPattern;
+ private int mSkippedSupplementalDataBytes;
private TrackOutputAdapter(int trackIndex) {
mTrackIndex = trackIndex;
@@ -1419,6 +1457,10 @@
throw new IllegalStateException();
}
}
+ } else if (sampleDataPart == SAMPLE_DATA_PART_SUPPLEMENTAL
+ && !mIncludeSupplementalData) {
+ mSkippedSupplementalDataBytes += length;
+ data.skipBytes(length);
} else {
outputSampleData(data, length);
}
@@ -1427,6 +1469,8 @@
@Override
public void sampleMetadata(
long timeUs, int flags, int size, int offset, @Nullable CryptoData cryptoData) {
+ size -= mSkippedSupplementalDataBytes;
+ mSkippedSupplementalDataBytes = 0;
mOutputConsumer.onSampleCompleted(
mTrackIndex,
timeUs,
@@ -1686,13 +1730,13 @@
}
}
- private static int getMediaParserFlags(int flags) {
+ private int getMediaParserFlags(int flags) {
@SampleFlags int result = 0;
result |= (flags & C.BUFFER_FLAG_ENCRYPTED) != 0 ? SAMPLE_FLAG_ENCRYPTED : 0;
result |= (flags & C.BUFFER_FLAG_KEY_FRAME) != 0 ? SAMPLE_FLAG_KEY_FRAME : 0;
result |= (flags & C.BUFFER_FLAG_DECODE_ONLY) != 0 ? SAMPLE_FLAG_DECODE_ONLY : 0;
result |=
- (flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0
+ (flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0 && mIncludeSupplementalData
? SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA
: 0;
result |= (flags & C.BUFFER_FLAG_LAST_SAMPLE) != 0 ? SAMPLE_FLAG_LAST_SAMPLE : 0;
@@ -1755,6 +1799,7 @@
expectedTypeByParameterName.put(PARAMETER_TS_DETECT_ACCESS_UNITS, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_IN_BAND_CRYPTO_INFO, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_INCLUDE_SUPPLEMENTAL_DATA, Boolean.class);
EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName);
}
}
diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp
index dbb5bd3d..fdb078e 100644
--- a/apex/sdkextensions/Android.bp
+++ b/apex/sdkextensions/Android.bp
@@ -39,7 +39,7 @@
sdk {
name: "sdkextensions-sdk",
- java_header_libs: [ "framework-sdkextensions-stubs-systemapi" ],
+ java_sdk_libs: [ "framework-sdkextensions" ],
}
apex_key {
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 14e23ed..b8aad7d 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -25,14 +25,18 @@
visibility: [ "//frameworks/base" ] // For the "global" stubs.
}
-java_library {
+java_sdk_library {
name: "framework-sdkextensions",
srcs: [ ":framework-sdkextensions-sources" ],
- sdk_version: "system_current",
- libs: [ "framework-annotations-lib" ],
+ defaults: ["framework-module-defaults"],
+
+ // TODO(b/155480189) - Remove naming_scheme once references have been resolved.
+ // Temporary java_sdk_library component naming scheme to use to ease the transition from separate
+ // modules to java_sdk_library.
+ naming_scheme: "framework-modules",
+
permitted_packages: [ "android.os.ext" ],
installable: true,
- plugins: ["java_api_finder"],
visibility: [
"//frameworks/base/apex/sdkextensions",
"//frameworks/base/apex/sdkextensions/testing",
@@ -43,102 +47,3 @@
"test_com.android.sdkext",
],
}
-
-stubs_defaults {
- name: "framework-sdkextensions-stubs-defaults",
- srcs: [ ":framework-sdkextensions-sources" ],
- libs: [ "framework-annotations-lib" ],
- dist: { dest: "framework-sdkextensions.txt" },
-}
-
-droidstubs {
- name: "framework-sdkextensions-stubs-srcs-publicapi",
- defaults: [
- "framework-module-stubs-defaults-publicapi",
- "framework-sdkextensions-stubs-defaults",
- ],
- check_api: {
- last_released: {
- api_file: ":framework-sdkextensions.api.public.latest",
- removed_api_file: ":framework-sdkextensions-removed.api.public.latest",
- },
- api_lint: {
- new_since: ":framework-sdkextensions.api.public.latest",
- },
- },
-}
-
-droidstubs {
- name: "framework-sdkextensions-stubs-srcs-systemapi",
- defaults: [
- "framework-module-stubs-defaults-systemapi",
- "framework-sdkextensions-stubs-defaults",
- ],
- check_api: {
- last_released: {
- api_file: ":framework-sdkextensions.api.system.latest",
- removed_api_file: ":framework-sdkextensions-removed.api.system.latest",
- },
- api_lint: {
- new_since: ":framework-sdkextensions.api.system.latest",
- },
- },
-}
-
-droidstubs {
- name: "framework-sdkextensions-api-module_libs_api",
- defaults: [
- "framework-module-api-defaults-module_libs_api",
- "framework-sdkextensions-stubs-defaults",
- ],
- check_api: {
- last_released: {
- api_file: ":framework-sdkextensions.api.module-lib.latest",
- removed_api_file: ":framework-sdkextensions-removed.api.module-lib.latest",
- },
- api_lint: {
- new_since: ":framework-sdkextensions.api.module-lib.latest",
- },
- },
-}
-
-droidstubs {
- name: "framework-sdkextensions-stubs-srcs-module_libs_api",
- defaults: [
- "framework-module-stubs-defaults-module_libs_api",
- "framework-sdkextensions-stubs-defaults",
- ],
-}
-
-java_library {
- name: "framework-sdkextensions-stubs-publicapi",
- srcs: [":framework-sdkextensions-stubs-srcs-publicapi"],
- defaults: ["framework-module-stubs-lib-defaults-publicapi"],
- visibility: [
- "//frameworks/base", // Framework
- "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
- ],
- dist: { dest: "framework-sdkextensions.jar" },
-}
-
-java_library {
- name: "framework-sdkextensions-stubs-systemapi",
- srcs: [":framework-sdkextensions-stubs-srcs-systemapi"],
- defaults: ["framework-module-stubs-lib-defaults-systemapi"],
- visibility: [
- "//frameworks/base", // Framework
- "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
- ],
- dist: { dest: "framework-sdkextensions.jar" },
-}
-
-java_library {
- name: "framework-sdkextensions-stubs-module_libs_api",
- srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"],
- defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
- visibility: [
- "//frameworks/base", // Framework
- "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
- ],
- dist: { dest: "framework-sdkextensions.jar" },
-}
diff --git a/api/current.txt b/api/current.txt
index 41a74cf..952ccda 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12097,6 +12097,7 @@
field public static final String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup";
field public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
field public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
+ field public static final String FEATURE_CONTROLS = "android.software.controls";
field public static final String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
field public static final String FEATURE_EMBEDDED = "android.hardware.type.embedded";
field public static final String FEATURE_ETHERNET = "android.hardware.ethernet";
diff --git a/api/test-current.txt b/api/test-current.txt
index 46049bd..5dc7bdb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1811,6 +1811,7 @@
}
public class ConnectivityManager {
+ method @RequiresPermission(anyOf={"android.permission.MANAGE_TEST_NETWORKS", android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 9e6d0a2..dc16125 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -351,9 +351,9 @@
Status IncidentService::registerSection(const int id, const String16& name16,
const sp<IIncidentDumpCallback>& callback) {
- const char* name = String8(name16).c_str();
+ const String8 name = String8(name16);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- ALOGI("Uid %d registers section %d '%s'", callingUid, id, name);
+ ALOGI("Uid %d registers section %d '%s'", callingUid, id, name.c_str());
if (callback == nullptr) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER);
}
@@ -363,11 +363,11 @@
ALOGW("Error registering section %d: calling uid does not match", id);
return Status::fromExceptionCode(Status::EX_SECURITY);
}
- mRegisteredSections.at(i) = new BringYourOwnSection(id, name, callingUid, callback);
+ mRegisteredSections.at(i) = new BringYourOwnSection(id, name.c_str(), callingUid, callback);
return Status::ok();
}
}
- mRegisteredSections.push_back(new BringYourOwnSection(id, name, callingUid, callback));
+ mRegisteredSections.push_back(new BringYourOwnSection(id, name.c_str(), callingUid, callback));
return Status::ok();
}
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 114cbb8..61e5eb0 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -876,7 +876,9 @@
status_t BringYourOwnSection::BlockingCall(unique_fd& pipeWriteFd) const {
android::os::ParcelFileDescriptor pfd(std::move(pipeWriteFd));
- mCallback->onDumpSection(pfd);
+ if(mCallback != nullptr) {
+ mCallback->onDumpSection(pfd);
+ }
return NO_ERROR;
}
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 2ce45ed..bc4909d 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -207,7 +207,7 @@
virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
private:
- const sp<IIncidentDumpCallback>& mCallback;
+ const sp<IIncidentDumpCallback> mCallback;
};
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index a65f5f7..4ffa040 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -826,7 +826,7 @@
uids.push_back(AID_SYSTEM);
}
vector<shared_ptr<LogEvent>> stats;
- if (mPullerManager->Pull(s, uids, &stats)) {
+ if (mPullerManager->Pull(s, uids, getElapsedRealtimeNs(), &stats)) {
for (const auto& it : stats) {
dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1d9f20e..f00a35d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -447,7 +447,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10080
+ // Next: 10082
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -538,6 +538,7 @@
SimSlotState sim_slot_state = 10078 [(module) = "telephony"];
SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"];
SettingSnapshot setting_snapshot = 10080 [(module) = "framework"];
+ DisplayWakeReason display_wake_reason = 10081 [(module) = "framework"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -1256,7 +1257,8 @@
*/
message ChargingStateChanged {
// State of the battery, from frameworks/base/core/proto/android/os/enums.proto.
- optional android.os.BatteryStatusEnum state = 1;
+ optional android.os.BatteryStatusEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -9460,9 +9462,6 @@
/** The ID of the entry that the users actioned on */
optional android.app.tvsettings.ItemId item_id = 2;
-
- /** Additional information (e.g., navigation direction on page focused) */
- optional string additional_info = 3;
}
/**
@@ -9634,6 +9633,17 @@
optional android.stats.accessibility.ServiceStatus service_status = 2;
}
+message DisplayWakeReason {
+ // Wake_up_reason code
+ // If LOWORD(wake_up_reason) = 0
+ // reference to HIWORD(wake_up_reason) PowerManager.WAKE_REASON_XXX
+ // else reference wake_up_reason to
+ // frameworks/base/services/core/java/com/android/server/power/Notifier.java#DispWakeupReason
+ optional int32 wake_up_reason = 1;
+ // Count of wake up by reason
+ optional int32 wake_times = 2;
+}
+
/**
* Logs app usage events.
*/
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 829a603..9df4d1f 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -38,14 +38,16 @@
mPullTimeoutNs(pullTimeoutNs),
mCoolDownNs(coolDownNs),
mAdditiveFields(additiveFields),
- mLastPullTimeNs(0) {
+ mLastPullTimeNs(0),
+ mLastEventTimeNs(0) {
}
-bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
+bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
lock_guard<std::mutex> lock(mLock);
int64_t elapsedTimeNs = getElapsedRealtimeNs();
StatsdStats::getInstance().notePull(mTagId);
- const bool shouldUseCache = elapsedTimeNs - mLastPullTimeNs < mCoolDownNs;
+ const bool shouldUseCache =
+ (mLastEventTimeNs == eventTimeNs) || (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs);
if (shouldUseCache) {
if (mHasGoodData) {
(*data) = mCachedData;
@@ -54,13 +56,13 @@
}
return mHasGoodData;
}
-
if (mLastPullTimeNs > 0) {
StatsdStats::getInstance().updateMinPullIntervalSec(
mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC);
}
mCachedData.clear();
mLastPullTimeNs = elapsedTimeNs;
+ mLastEventTimeNs = eventTimeNs;
mHasGoodData = PullInternal(&mCachedData);
if (!mHasGoodData) {
return mHasGoodData;
@@ -70,7 +72,7 @@
const bool pullTimeOut = pullDurationNs > mPullTimeoutNs;
if (pullTimeOut) {
// Something went wrong. Discard the data.
- clearCacheLocked();
+ mCachedData.clear();
mHasGoodData = false;
StatsdStats::getInstance().notePullTimeout(mTagId);
ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId,
@@ -104,6 +106,7 @@
int ret = mCachedData.size();
mCachedData.clear();
mLastPullTimeNs = 0;
+ mLastEventTimeNs = 0;
return ret;
}
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index fee571c..470d15e 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -51,7 +51,7 @@
// 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
// If a metric wants to make any change to the data, like timestamps, it
// should make a copy as this data may be shared with multiple metrics.
- bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
+ bool Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
int ForceClearCache();
@@ -94,6 +94,11 @@
int64_t mLastPullTimeNs;
+ // All pulls happen due to an event (app upgrade, bucket boundary, condition change, etc).
+ // If multiple pulls need to be done at the same event time, we will always use the cache after
+ // the first pull.
+ int64_t mLastEventTimeNs;
+
// Cache of data from last pull. If next request comes before cool down finishes,
// cached data will be returned.
// Cached data is cleared when
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 1a52eb9..8a9ec74 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -91,20 +91,21 @@
mPullAtomCallbackDeathRecipient(AIBinder_DeathRecipient_new(pullAtomCallbackDied)) {
}
-bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey,
+bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
vector<shared_ptr<LogEvent>>* data, bool useUids) {
std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, configKey, data, useUids);
+ return PullLocked(tagId, configKey, eventTimeNs, data, useUids);
}
-bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids,
+bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool useUids) {
std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, uids, data, useUids);
+ return PullLocked(tagId, uids, eventTimeNs, data, useUids);
}
bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
- vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
+ bool useUids) {
vector<int32_t> uids;
if (useUids) {
auto uidProviderIt = mPullUidProviders.find(configKey);
@@ -123,18 +124,19 @@
}
uids = pullUidProvider->getPullAtomUids(tagId);
}
- return PullLocked(tagId, uids, data, useUids);
+ return PullLocked(tagId, uids, eventTimeNs, data, useUids);
}
bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
- vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
+ bool useUids) {
VLOG("Initiating pulling %d", tagId);
if (useUids) {
for (int32_t uid : uids) {
PullerKey key = {.atomTag = tagId, .uid = uid};
auto pullerIt = kAllPullAtomInfo.find(key);
if (pullerIt != kAllPullAtomInfo.end()) {
- bool ret = pullerIt->second->Pull(data);
+ bool ret = pullerIt->second->Pull(eventTimeNs, data);
VLOG("pulled %zu items", data->size());
if (!ret) {
StatsdStats::getInstance().notePullFailed(tagId);
@@ -149,7 +151,7 @@
PullerKey key = {.atomTag = tagId, .uid = -1};
auto pullerIt = kAllPullAtomInfo.find(key);
if (pullerIt != kAllPullAtomInfo.end()) {
- bool ret = pullerIt->second->Pull(data);
+ bool ret = pullerIt->second->Pull(eventTimeNs, data);
VLOG("pulled %zu items", data->size());
if (!ret) {
StatsdStats::getInstance().notePullFailed(tagId);
@@ -290,7 +292,8 @@
}
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
- bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey, &data);
+ bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey,
+ elapsedTimeNs, &data);
if (!pullSuccess) {
VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
}
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 5e18aaa..194a0f5 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -101,11 +101,11 @@
// registered for any of the uids for this atom.
// If the metric wants to make any change to the data, like timestamps, they
// should make a copy as this data may be shared with multiple metrics.
- virtual bool Pull(int tagId, const ConfigKey& configKey,
+ virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
// Same as above, but directly specify the allowed uids to pull from.
- virtual bool Pull(int tagId, const vector<int32_t>& uids,
+ virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
// Clear pull data cache immediately.
@@ -152,11 +152,11 @@
// mapping from Config Key to the PullUidProvider for that config
std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
- bool PullLocked(int tagId, const ConfigKey& configKey, vector<std::shared_ptr<LogEvent>>* data,
- bool useUids = true);
+ bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
- bool PullLocked(int tagId, const vector<int32_t>& uids, vector<std::shared_ptr<LogEvent>>* data,
- bool useUids);
+ bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids);
// locks for data receiver and StatsCompanionService changes
std::mutex mLock;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index cc4c565..1d4d0b3 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -321,7 +321,7 @@
return;
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, mConfigKey, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
return;
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index e5ec72e..bf636a4 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -508,7 +508,7 @@
void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, mConfigKey, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
invalidateCurrentBucket(timestampNs, BucketDropReason::PULL_FAILED);
return;
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index 7b68721..361b161 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -152,6 +152,7 @@
}
int64_t nowMillis = getElapsedRealtimeMillis();
+ int64_t nowNanos = getElapsedRealtimeNs();
for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) {
if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval >= nowMillis) {
continue;
@@ -161,7 +162,7 @@
getUidsForPullAtom(&uids, pullInfo);
vector<std::shared_ptr<LogEvent>> data;
- mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, &data);
+ mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, nowNanos, &data);
VLOG("Pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id());
writePulledAtomsLocked(data, pullInfo.mPullerMatcher);
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index e37c72e..13cdfc2 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -155,7 +155,7 @@
TEST_F(StatsCallbackPullerTest, PullTimeout) {
shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
pullSuccess = true;
- pullDelayNs = 500000000; // 500ms.
+ pullDelayNs = MillisToNano(5); // 5ms.
pullTimeoutNs = 10000; // 10 microseconds.
int64_t value = 4321;
values.push_back(value);
@@ -184,7 +184,7 @@
TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) {
shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
pullSuccess = true;
- pullDelayNs = 500000000; // 500 ms.
+ pullDelayNs = MillisToNano(5); // 5 ms.
pullTimeoutNs = 10000; // 10 microsseconds.
int64_t value = 4321;
int32_t uid = 123;
@@ -196,7 +196,7 @@
vector<shared_ptr<LogEvent>> dataHolder;
int64_t startTimeNs = getElapsedRealtimeNs();
// Returns false, since StatsPuller code will evaluate the timeout.
- EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, &dataHolder));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, startTimeNs, &dataHolder));
int64_t endTimeNs = getElapsedRealtimeNs();
int64_t actualPullDurationNs = endTimeNs - startTimeNs;
diff --git a/cmds/statsd/tests/external/StatsPullerManager_test.cpp b/cmds/statsd/tests/external/StatsPullerManager_test.cpp
index 6b3f4cc..c76e85e 100644
--- a/cmds/statsd/tests/external/StatsPullerManager_test.cpp
+++ b/cmds/statsd/tests/external/StatsPullerManager_test.cpp
@@ -101,14 +101,14 @@
sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data, true));
}
TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) {
sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
vector<shared_ptr<LogEvent>> data;
- EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, &data, true));
+ EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data, true));
ASSERT_EQ(data.size(), 1);
EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
ASSERT_EQ(data[0]->getValues().size(), 1);
@@ -121,7 +121,7 @@
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data, true));
}
TEST(StatsPullerManagerTest, TestPullConfigKeyGood) {
@@ -130,7 +130,7 @@
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, &data, true));
+ EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data, true));
EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
ASSERT_EQ(data[0]->getValues().size(), 1);
EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2);
@@ -142,7 +142,7 @@
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data, true));
}
} // namespace statsd
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
index 02a43e4..55a9036 100644
--- a/cmds/statsd/tests/external/StatsPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsPuller_test.cpp
@@ -39,7 +39,6 @@
using testing::Contains;
namespace {
-// cooldown time 1sec.
int pullTagId = 10014;
bool pullSuccess;
@@ -48,7 +47,8 @@
class FakePuller : public StatsPuller {
public:
- FakePuller() : StatsPuller(pullTagId, /*coolDown=*/NS_PER_SEC, /*timeout=*/NS_PER_SEC / 2){};
+ FakePuller()
+ : StatsPuller(pullTagId, /*coolDownNs=*/MillisToNano(10), /*timeoutNs=*/MillisToNano(5)){};
private:
bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override {
@@ -92,21 +92,21 @@
pullSuccess = true;
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_TRUE(puller.Pull(&dataHolder));
+ EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(1, dataHolder.size());
EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
ASSERT_EQ(1, dataHolder[0]->size());
EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
- sleep_for(std::chrono::seconds(1));
+ sleep_for(std::chrono::milliseconds(11));
pullData.clear();
pullData.push_back(createSimpleEvent(2222L, 44));
pullSuccess = true;
- EXPECT_TRUE(puller.Pull(&dataHolder));
+ EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(1, dataHolder.size());
EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs());
@@ -120,26 +120,27 @@
pullSuccess = true;
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_TRUE(puller.Pull(&dataHolder));
+ EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(1, dataHolder.size());
EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
ASSERT_EQ(1, dataHolder[0]->size());
EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
- sleep_for(std::chrono::seconds(1));
+ sleep_for(std::chrono::milliseconds(11));
pullData.clear();
pullData.push_back(createSimpleEvent(2222L, 44));
pullSuccess = false;
dataHolder.clear();
- EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(0, dataHolder.size());
+ // Fails due to hitting the cool down.
pullSuccess = true;
dataHolder.clear();
- EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(0, dataHolder.size());
}
@@ -147,19 +148,20 @@
TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) {
pullData.push_back(createSimpleEvent(1111L, 33));
pullSuccess = true;
- // timeout is 0.5
- pullDelayNs = (long)(0.8 * NS_PER_SEC);
+ // timeout is 5ms
+ pullDelayNs = MillisToNano(6);
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(0, dataHolder.size());
pullData.clear();
pullData.push_back(createSimpleEvent(2222L, 44));
+ pullDelayNs = 0;
pullSuccess = true;
dataHolder.clear();
- EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(0, dataHolder.size());
}
@@ -169,7 +171,7 @@
pullSuccess = false;
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(0, dataHolder.size());
}
@@ -177,10 +179,10 @@
pullData.push_back(createSimpleEvent(1111L, 33));
pullSuccess = true;
- pullDelayNs = NS_PER_SEC;
+ pullDelayNs = MillisToNano(6);
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(0, dataHolder.size());
}
@@ -190,7 +192,7 @@
pullSuccess = true;
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_TRUE(puller.Pull(&dataHolder));
+ EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(1, dataHolder.size());
EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
@@ -203,7 +205,7 @@
pullSuccess = true;
dataHolder.clear();
- EXPECT_TRUE(puller.Pull(&dataHolder));
+ EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(1, dataHolder.size());
EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
@@ -217,7 +219,7 @@
pullSuccess = false;
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
ASSERT_EQ(0, dataHolder.size());
pullData.clear();
@@ -225,7 +227,84 @@
pullSuccess = true;
- EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullSameEventTime) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+
+ pullSuccess = true;
+ int64_t eventTimeNs = getElapsedRealtimeNs();
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_TRUE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(1, dataHolder.size());
+ EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+ EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+ ASSERT_EQ(1, dataHolder[0]->size());
+ EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+
+ // Sleep to ensure the cool down expires.
+ sleep_for(std::chrono::milliseconds(11));
+ pullSuccess = true;
+
+ dataHolder.clear();
+ EXPECT_TRUE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(1, dataHolder.size());
+ EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+ EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+ ASSERT_EQ(1, dataHolder[0]->size());
+ EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+// Test pull takes longer than timeout, 2nd pull happens at same event time
+TEST_F(StatsPullerTest, PullTakeTooLongAndPullSameEventTime) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+ pullSuccess = true;
+ int64_t eventTimeNs = getElapsedRealtimeNs();
+ // timeout is 5ms
+ pullDelayNs = MillisToNano(6);
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
+
+ // Sleep to ensure the cool down expires. 6ms is taken by the delay, so only 5 is needed here.
+ sleep_for(std::chrono::milliseconds(5));
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+ pullDelayNs = 0;
+
+ pullSuccess = true;
+ dataHolder.clear();
+ EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullFailsAndPullSameEventTime) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+
+ pullSuccess = false;
+ int64_t eventTimeNs = getElapsedRealtimeNs();
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
+
+ // Sleep to ensure the cool down expires.
+ sleep_for(std::chrono::milliseconds(11));
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+
+ pullSuccess = true;
+
+ EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
ASSERT_EQ(0, dataHolder.size());
}
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 12839e6..cc5f459 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -138,11 +138,12 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
- data->push_back(makeLogEvent(tagId, bucketStartTimeNs + 10, 3, "some value", 11));
+ data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11));
return true;
}));
@@ -311,15 +312,15 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
.WillOnce(Return(false))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 2));
- return true;
- }));
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2));
+ return true;
+ }));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
@@ -389,7 +390,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)).WillOnce(Return(false));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Return(false));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
@@ -435,14 +437,16 @@
new EventMatcherWizard({new SimpleLogMatchingTracker(
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ int64_t conditionChangeNs = bucketStartTimeNs + 8;
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 100));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100));
return true;
}));
@@ -451,7 +455,7 @@
bucketStartTimeNs, pullerManager);
gaugeProducer.prepareFirstBucket();
- gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+ gaugeProducer.onConditionChanged(true, conditionChangeNs);
ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
@@ -519,14 +523,16 @@
return ConditionState::kTrue;
}));
+ int64_t sliceConditionChangeNs = bucketStartTimeNs + 8;
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 10, 1000, 100));
+ data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100));
return true;
}));
@@ -535,7 +541,7 @@
bucketStartTimeNs, pullerManager);
gaugeProducer.prepareFirstBucket();
- gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
+ gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs);
ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first;
@@ -560,7 +566,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)).WillOnce(Return(false));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Return(false));
GaugeMetric metric;
metric.set_id(metricId);
@@ -658,17 +665,19 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 4));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
return true;
}))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 5));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
return true;
}))
.WillOnce(Return(true));
@@ -727,23 +736,26 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 3, 3, 4));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 10, 4, 5));
+ data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4));
return true;
}))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 4, 6));
+ data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
+ data->clear();
+ data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6));
return true;
}))
.WillOnce(Return(true));
@@ -801,14 +813,14 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _, _))
// Bucket start.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 10));
- return true;
- }));
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10));
+ return true;
+ }));
int triggerId = 5;
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index be1b9eb..b6e1075 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -278,13 +278,13 @@
TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -352,18 +352,20 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Initialize bucket.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
return true;
}))
// Partial bucket.
- .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
- vector<std::shared_ptr<LogEvent>>* data,
- bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](
+ int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(
CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs + 8, 5));
@@ -416,13 +418,13 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
- return true;
- }));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
@@ -487,7 +489,8 @@
metric.set_use_absolute_value_on_reset(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Return(true));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -546,7 +549,8 @@
TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)).WillOnce(Return(false));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Return(false));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -601,21 +605,24 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130));
return true;
}))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 1); // Third condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180));
return true;
@@ -745,11 +752,12 @@
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
.WillOnce(Return(true))
- .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
- vector<std::shared_ptr<LogEvent>>* data,
- bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](
+ int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120));
return true;
@@ -803,7 +811,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Return(true));
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
@@ -825,15 +834,18 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
return true;
}))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs,
+ bucket2StartTimeNs - 100); // Condition change to false time.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 120));
return true;
@@ -1049,7 +1061,8 @@
TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Return(true));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -1119,17 +1132,19 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}))
// condition becomes false
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
return true;
@@ -1179,24 +1194,27 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}))
// condition becomes false
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
return true;
}))
// condition becomes true again
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 25);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130));
return true;
@@ -1625,13 +1643,13 @@
metric.set_use_zero_default_base(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
- return true;
- }));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -1701,13 +1719,13 @@
metric.set_use_zero_default_base(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
- return true;
- }));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -1806,13 +1824,13 @@
metric.mutable_dimensions_in_what()->add_child()->set_field(1);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
- return true;
- }));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -1909,8 +1927,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// Used by onConditionChanged.
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -1942,9 +1960,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // Condition change to true.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
@@ -1979,15 +1998,17 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50));
- return false;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50));
+ return false;
+ }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to false.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
@@ -2019,8 +2040,8 @@
metric.set_max_pull_delay_sec(0);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 120));
@@ -2067,8 +2088,8 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
@@ -2100,12 +2121,13 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First onConditionChanged
.WillOnce(Return(false))
// Second onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
return true;
@@ -2177,9 +2199,9 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _, _))
// First onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
for (int i = 0; i < 2000; i++) {
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
@@ -2234,17 +2256,19 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
return true;
}))
// Second onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
return true;
@@ -2312,17 +2336,19 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
return true;
}))
// Second onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
return true;
@@ -2384,14 +2410,14 @@
TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
// Start bucket.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }));
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -2417,19 +2443,21 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First onConditionChanged
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- return true;
- }));
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+ data->clear();
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
@@ -2457,26 +2485,29 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First onConditionChanged
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2));
- return true;
- }))
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5));
- return true;
- }));
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 11);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 12);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
@@ -2517,14 +2548,14 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
// First onConditionChanged
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }));
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
@@ -2559,18 +2590,20 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2;
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Initialization.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }))
// notifyAppUpgrade.
- .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
- vector<std::shared_ptr<LogEvent>>* data,
- bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](
+ int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
return true;
@@ -2609,17 +2642,19 @@
TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Second onConditionChanged.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
return true;
}))
// Third onConditionChanged.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7));
return true;
@@ -2678,14 +2713,14 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
// Initialization.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }));
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -2708,18 +2743,20 @@
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Initialization.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }))
// notifyAppUpgrade.
- .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
- vector<std::shared_ptr<LogEvent>>* data,
- bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](
+ int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
return true;
@@ -2746,21 +2783,23 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First on condition changed.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }))
// Second on condition changed.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }));
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
@@ -2788,28 +2827,31 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First condition change.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }))
// 2nd condition change.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
- return true;
- }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 8);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
+ return true;
+ }))
// 3rd condition change.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
- return true;
- }));
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
+ return true;
+ }));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
@@ -2848,9 +2890,9 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
// Initial pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
@@ -2886,9 +2928,9 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
// Initial pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
@@ -2930,16 +2972,18 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Initial pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
}))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(
CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10, tagId, 3, 3));
@@ -2984,17 +3028,19 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
return true;
}))
// condition becomes false
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20));
return true;
@@ -3032,9 +3078,9 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
@@ -3083,10 +3129,11 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
return true;
@@ -3122,9 +3169,9 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _, _))
// Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10));
@@ -3167,9 +3214,9 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _, _))
// Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3220,17 +3267,19 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
return true;
}))
// Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 100);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15));
return true;
@@ -3283,17 +3332,19 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
return true;
}))
// Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10000);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15));
return true;
@@ -3337,10 +3388,11 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
return true;
@@ -3385,17 +3437,19 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
return true;
}))
// Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket4StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1000, 15));
return true;
@@ -3441,17 +3495,19 @@
metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
return true;
}))
// Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 9000000);
data->clear();
data->push_back(
CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 9000000, 15));
@@ -3494,9 +3550,9 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
// Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3545,10 +3601,11 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First condition change event.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
for (int i = 0; i < 2000; i++) {
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
}
@@ -3563,8 +3620,9 @@
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Return(false))
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 220);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 220, 10));
return true;
@@ -3662,38 +3720,43 @@
// Set up ValueMetricProducer.
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// ValueMetricProducer initialized.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }))
- // Screen state change to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Screen state change to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
return true;
}))
// Screen state change to OFF.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9));
return true;
}))
// Screen state change to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
return true;
}))
// Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
return true;
@@ -3818,38 +3881,43 @@
// Set up ValueMetricProducer.
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// ValueMetricProducer initialized.
- .WillOnce(Invoke(
- [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }))
- // Screen state change to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Screen state change to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
return true;
}))
// Screen state change to VR.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9));
return true;
}))
// Screen state change to OFF.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
return true;
}))
// Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
return true;
@@ -3990,18 +4058,20 @@
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// ValueMetricProducer initialized.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 7));
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1 /*uid*/, 3));
return true;
}))
// Uid 1 process state change from kStateUnknown -> Foreground
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
data->push_back(
CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 1 /*uid*/, 6));
@@ -4012,8 +4082,9 @@
return true;
}))
// Uid 2 process state change from kStateUnknown -> Background
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40);
data->clear();
data->push_back(
CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 2 /*uid*/, 9));
@@ -4024,8 +4095,9 @@
return true;
}))
// Uid 1 process state change from Foreground -> Background
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20);
data->clear();
data->push_back(
CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 1 /*uid*/, 13));
@@ -4036,8 +4108,9 @@
return true;
}))
// Uid 1 process state change from Background -> Foreground
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40);
data->clear();
data->push_back(
CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 1 /*uid*/, 17));
@@ -4048,8 +4121,9 @@
return true;
}))
// Dump report pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&,
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50);
data->clear();
data->push_back(
CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 2 /*uid*/, 20));
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 46ef0f6..eeb38a4 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -38,10 +38,11 @@
int64_t nextPulltimeNs, int64_t intervalNs));
MOCK_METHOD3(UnRegisterReceiver,
void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver));
- MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key,
+ MOCK_METHOD5(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool useUids));
- MOCK_METHOD4(Pull, bool(const int pullCode, const vector<int32_t>& uids,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids));
+ MOCK_METHOD5(Pull,
+ bool(const int pullCode, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids));
MOCK_METHOD2(RegisterPullUidProvider,
void(const ConfigKey& configKey, wp<PullUidProvider> provider));
MOCK_METHOD2(UnregisterPullUidProvider,
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index 363fcb4..e384b6a 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -190,8 +190,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
const vector<int32_t> uids = {AID_SYSTEM};
- EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _))
- .WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&,
+ EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _, _))
+ .WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&, const int64_t,
vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1));
diff --git a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
index c35f7fc..3b14be7 100644
--- a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
+++ b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
@@ -16,6 +16,7 @@
package com.android.commands.uiautomator;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.UiAutomation;
import android.graphics.Point;
import android.hardware.display.DisplayManagerGlobal;
@@ -61,11 +62,14 @@
public void run(String[] args) {
File dumpFile = DEFAULT_DUMP_FILE;
boolean verboseMode = true;
+ boolean allWindows = false;
for (String arg : args) {
if (arg.equals("--compressed"))
verboseMode = false;
- else if (!arg.startsWith("-")) {
+ else if (arg.equals("--windows")) {
+ allWindows = true;
+ } else if (!arg.startsWith("-")) {
dumpFile = new File(arg);
}
}
@@ -85,18 +89,28 @@
try {
UiAutomation uiAutomation = automationWrapper.getUiAutomation();
uiAutomation.waitForIdle(1000, 1000 * 10);
- AccessibilityNodeInfo info = uiAutomation.getRootInActiveWindow();
- if (info == null) {
- System.err.println("ERROR: null root node returned by UiTestAutomationBridge.");
- return;
- }
+ if (allWindows) {
+ AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+ info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+ uiAutomation.setServiceInfo(info);
+ AccessibilityNodeInfoDumper.dumpWindowsToFile(
+ uiAutomation.getWindowsOnAllDisplays(), dumpFile,
+ DisplayManagerGlobal.getInstance());
+ } else {
+ AccessibilityNodeInfo info = uiAutomation.getRootInActiveWindow();
+ if (info == null) {
+ System.err.println("ERROR: null root node returned by UiTestAutomationBridge.");
+ return;
+ }
- Display display =
- DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
- int rotation = display.getRotation();
- Point size = new Point();
- display.getSize(size);
- AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x, size.y);
+ Display display =
+ DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
+ int rotation = display.getRotation();
+ Point size = new Point();
+ display.getSize(size);
+ AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x,
+ size.y);
+ }
} catch (TimeoutException re) {
System.err.println("ERROR: could not get idle state.");
return;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index 63c51e8..ab198b3 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -16,11 +16,17 @@
package com.android.uiautomator.core;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Environment;
import android.os.SystemClock;
import android.util.Log;
+import android.util.SparseArray;
import android.util.Xml;
+import android.view.Display;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
import org.xmlpull.v1.XmlSerializer;
@@ -28,6 +34,7 @@
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
+import java.util.List;
/**
*
@@ -98,6 +105,95 @@
Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms");
}
+ /**
+ * Using {@link AccessibilityWindowInfo} this method will dump some window information and
+ * then walk the layout hierarchy of it's
+ * and generates an xml dump to the location specified by <code>dumpFile</code>
+ * @param allWindows All windows indexed by display-id.
+ * @param dumpFile The file to dump to.
+ */
+ public static void dumpWindowsToFile(SparseArray<List<AccessibilityWindowInfo>> allWindows,
+ File dumpFile, DisplayManagerGlobal displayManager) {
+ if (allWindows.size() == 0) {
+ return;
+ }
+ final long startTime = SystemClock.uptimeMillis();
+ try {
+ FileWriter writer = new FileWriter(dumpFile);
+ XmlSerializer serializer = Xml.newSerializer();
+ StringWriter stringWriter = new StringWriter();
+ serializer.setOutput(stringWriter);
+ serializer.startDocument("UTF-8", true);
+ serializer.startTag("", "displays");
+ for (int d = 0, nd = allWindows.size(); d < nd; ++d) {
+ int displayId = allWindows.keyAt(d);
+ Display display = displayManager.getRealDisplay(displayId);
+ if (display == null) {
+ continue;
+ }
+ final List<AccessibilityWindowInfo> windows = allWindows.valueAt(d);
+ if (windows.isEmpty()) {
+ continue;
+ }
+ serializer.startTag("", "display");
+ serializer.attribute("", "id", Integer.toString(displayId));
+ int rotation = display.getRotation();
+ Point size = new Point();
+ display.getSize(size);
+ for (int i = 0, n = windows.size(); i < n; ++i) {
+ dumpWindowRec(windows.get(i), serializer, i, size.x, size.y, rotation);
+ }
+ serializer.endTag("", "display");
+ }
+ serializer.endTag("", "displays");
+ serializer.endDocument();
+ writer.write(stringWriter.toString());
+ writer.close();
+ } catch (IOException e) {
+ Log.e(LOGTAG, "failed to dump window to file", e);
+ }
+ final long endTime = SystemClock.uptimeMillis();
+ Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms");
+ }
+
+ private static void dumpWindowRec(AccessibilityWindowInfo winfo, XmlSerializer serializer,
+ int index, int width, int height, int rotation) throws IOException {
+ serializer.startTag("", "window");
+ serializer.attribute("", "index", Integer.toString(index));
+ final CharSequence title = winfo.getTitle();
+ serializer.attribute("", "title", title != null ? title.toString() : "");
+ final Rect tmpBounds = new Rect();
+ winfo.getBoundsInScreen(tmpBounds);
+ serializer.attribute("", "bounds", tmpBounds.toShortString());
+ serializer.attribute("", "active", Boolean.toString(winfo.isActive()));
+ serializer.attribute("", "focused", Boolean.toString(winfo.isFocused()));
+ serializer.attribute("", "accessibility-focused",
+ Boolean.toString(winfo.isAccessibilityFocused()));
+ serializer.attribute("", "id", Integer.toString(winfo.getId()));
+ serializer.attribute("", "layer", Integer.toString(winfo.getLayer()));
+ serializer.attribute("", "type", AccessibilityWindowInfo.typeToString(winfo.getType()));
+ int count = winfo.getChildCount();
+ for (int i = 0; i < count; ++i) {
+ AccessibilityWindowInfo child = winfo.getChild(i);
+ if (child == null) {
+ Log.i(LOGTAG, String.format("Null window child %d/%d, parent: %s", i, count,
+ winfo.getTitle()));
+ continue;
+ }
+ dumpWindowRec(child, serializer, i, width, height, rotation);
+ child.recycle();
+ }
+ AccessibilityNodeInfo root = winfo.getRoot();
+ if (root != null) {
+ serializer.startTag("", "hierarchy");
+ serializer.attribute("", "rotation", Integer.toString(rotation));
+ dumpNodeRec(root, serializer, 0, width, height);
+ root.recycle();
+ serializer.endTag("", "hierarchy");
+ }
+ serializer.endTag("", "window");
+ }
+
private static void dumpNodeRec(AccessibilityNodeInfo node, XmlSerializer serializer,int index,
int width, int height) throws IOException {
serializer.startTag("", "node");
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9067069..b0ce7d1 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2231,7 +2231,8 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mId);
ComponentName.writeToParcel(mTopActivityComponent, dest);
- dest.writeParcelable(mSnapshot, 0);
+ dest.writeParcelable(mSnapshot != null && !mSnapshot.isDestroyed() ? mSnapshot : null,
+ 0);
dest.writeInt(mColorSpace.getId());
dest.writeInt(mOrientation);
dest.writeInt(mRotation);
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 745add1..79da1f6 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1183,7 +1183,8 @@
return NO_MATCH_DATA;
}
}
- if (mPort >= 0) {
+ // if we're dealing with wildcard support, we ignore ports
+ if (!wildcardSupported && mPort >= 0) {
if (mPort != data.getPort()) {
return NO_MATCH_DATA;
}
@@ -1580,12 +1581,13 @@
* @param wildcardSupported if true, will allow parameters to use wildcards
*/
private int matchData(String type, String scheme, Uri data, boolean wildcardSupported) {
- final ArrayList<String> types = mDataTypes;
+ final boolean wildcardWithMimegroups = wildcardSupported && countMimeGroups() != 0;
+ final List<String> types = mDataTypes;
final ArrayList<String> schemes = mDataSchemes;
int match = MATCH_CATEGORY_EMPTY;
- if (types == null && schemes == null) {
+ if (!wildcardWithMimegroups && types == null && schemes == null) {
return ((type == null && data == null)
? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
}
@@ -1640,7 +1642,9 @@
}
}
- if (types != null) {
+ if (wildcardWithMimegroups) {
+ return MATCH_CATEGORY_TYPE;
+ } else if (types != null) {
if (findMimeType(type)) {
match = MATCH_CATEGORY_TYPE;
} else {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4d718ef..1e9cddbb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3049,6 +3049,16 @@
public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports a system interface for the user to select
+ * and bind device control services provided by applications.
+ *
+ * @see android.service.controls.ControlsProviderService
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CONTROLS = "android.software.controls";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
* the requisite hardware support to support reboot escrow of synthetic password for updates.
*
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 2012039..b149d77 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -43,6 +43,10 @@
* through the {@link CameraManager CameraManager}
* interface with {@link CameraManager#getCameraCharacteristics}.</p>
*
+ * <p>When obtained by a client that does not hold the CAMERA permission, some metadata values are
+ * not included. The list of keys that require the permission is given by
+ * {@link #getKeysNeedingPermission}.</p>
+ *
* <p>{@link CameraCharacteristics} objects are immutable.</p>
*
* @see CameraDevice
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index e81c649..230aa04 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -362,6 +362,11 @@
* cameras that can only be used as part of logical multi-camera. These cameras cannot be
* opened directly via {@link #openCamera}</p>
*
+ * <p>Also starting with API level 29, while most basic camera information is still available
+ * even without the CAMERA permission, some values are not available to apps that do not hold
+ * that permission. The keys not available are listed by
+ * {@link CameraCharacteristics#getKeysNeedingPermission}.</p>
+ *
* @param cameraId The id of the camera device to query. This could be either a standalone
* camera ID which can be directly opened by {@link #openCamera}, or a physical camera ID that
* can only used as part of a logical multi-camera.
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index ae04693..b546967 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -3949,14 +3949,24 @@
new Key<Integer>("android.sensor.testPatternMode", int.class);
/**
- * <p>Duration between the start of first row exposure
- * and the start of last row exposure.</p>
- * <p>This is the exposure time skew between the first and last
- * row exposure start times. The first row and the last row are
- * the first and last rows inside of the
+ * <p>Duration between the start of exposure for the first row of the image sensor,
+ * and the start of exposure for one past the last row of the image sensor.</p>
+ * <p>This is the exposure time skew between the first and <code>(last+1)</code> row exposure start times. The
+ * first row and the last row are the first and last rows inside of the
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
- * <p>For typical camera sensors that use rolling shutters, this is also equivalent
- * to the frame readout time.</p>
+ * <p>For typical camera sensors that use rolling shutters, this is also equivalent to the frame
+ * readout time.</p>
+ * <p>If the image sensor is operating in a binned or cropped mode due to the current output
+ * target resolutions, it's possible this skew is reported to be larger than the exposure
+ * time, for example, since it is based on the full array even if a partial array is read
+ * out. Be sure to scale the number to cover the section of the sensor actually being used
+ * for the outputs you care about. So if your output covers N rows of the active array of
+ * height H, scale this value by N/H to get the total skew for that viewport.</p>
+ * <p><em>Note:</em> Prior to Android 11, this field was described as measuring duration from
+ * first to last row of the image sensor, which is not equal to the frame readout time for a
+ * rolling shutter sensor. Implementations generally reported the latter value, so to resolve
+ * the inconsistency, the description has been updated to range from (first, last+1) row
+ * exposure start, instead.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* >= 0 and <
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 7332ede..36ffe50 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -48,6 +48,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -4693,4 +4694,28 @@
Log.d(TAG, "StackLog:" + sb.toString());
}
}
+
+ /**
+ * Simulates a Data Stall for the specified Network.
+ *
+ * <p>The caller must be the owner of the specified Network.
+ *
+ * @param detectionMethod The detection method used to identify the Data Stall.
+ * @param timestampMillis The timestamp at which the stall 'occurred', in milliseconds.
+ * @param network The Network for which a Data Stall is being simluated.
+ * @param extras The PersistableBundle of extras included in the Data Stall notification.
+ * @throws SecurityException if the caller is not the owner of the given network.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_TEST_NETWORKS,
+ android.Manifest.permission.NETWORK_STACK})
+ public void simulateDataStall(int detectionMethod, long timestampMillis,
+ @NonNull Network network, @NonNull PersistableBundle extras) {
+ try {
+ mService.simulateDataStall(detectionMethod, timestampMillis, network, extras);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1434560..69a47f2 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.net.ConnectionInfo;
+import android.net.ConnectivityDiagnosticsManager;
import android.net.IConnectivityDiagnosticsCallback;
import android.net.LinkProperties;
import android.net.Network;
@@ -33,6 +34,7 @@
import android.os.IBinder;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.ResultReceiver;
import com.android.internal.net.LegacyVpnInfo;
@@ -227,4 +229,7 @@
void unregisterConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback);
IBinder startOrGetTestNetworkService();
+
+ void simulateDataStall(int detectionMethod, long timestampMillis, in Network network,
+ in PersistableBundle extras);
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 52d6fdf..9ded22f 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -677,16 +677,27 @@
* restrictions.
* @hide
*/
- public void restrictCapabilitesForTestNetwork() {
+ public void restrictCapabilitesForTestNetwork(int creatorUid) {
final long originalCapabilities = mNetworkCapabilities;
final NetworkSpecifier originalSpecifier = mNetworkSpecifier;
final int originalSignalStrength = mSignalStrength;
+ final int originalOwnerUid = getOwnerUid();
+ final int[] originalAdministratorUids = getAdministratorUids();
clearAll();
// Reset the transports to only contain TRANSPORT_TEST.
mTransportTypes = (1 << TRANSPORT_TEST);
mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
mNetworkSpecifier = originalSpecifier;
mSignalStrength = originalSignalStrength;
+
+ // Only retain the owner and administrator UIDs if they match the app registering the remote
+ // caller that registered the network.
+ if (originalOwnerUid == creatorUid) {
+ setOwnerUid(creatorUid);
+ }
+ if (ArrayUtils.contains(originalAdministratorUids, creatorUid)) {
+ setAdministratorUids(new int[] {creatorUid});
+ }
}
/**
diff --git a/core/java/android/os/IIncidentDumpCallback.aidl b/core/java/android/os/IIncidentDumpCallback.aidl
index 09b5b01..d94df34 100644
--- a/core/java/android/os/IIncidentDumpCallback.aidl
+++ b/core/java/android/os/IIncidentDumpCallback.aidl
@@ -25,7 +25,8 @@
*/
oneway interface IIncidentDumpCallback {
/**
- * Dumps section data to the given ParcelFileDescriptor.
+ * Dumps section data to the given ParcelFileDescriptor, which needs to be
+ * closed properly after writing the data.
*/
void onDumpSection(in ParcelFileDescriptor fd);
}
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 321dc9e..958c7fb 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -92,10 +92,7 @@
}
}
- if (!result.mDefaultStorage.startLoading()) {
- // TODO(b/146080380): add incremental-specific error code
- throw new IOException("Failed to start loading data for Incremental installation.");
- }
+ result.startLoading();
return result;
}
@@ -144,6 +141,15 @@
}
/**
+ * Starts or re-starts loading of data.
+ */
+ public void startLoading() throws IOException {
+ if (!mDefaultStorage.startLoading()) {
+ throw new IOException("Failed to start loading data for Incremental installation.");
+ }
+ }
+
+ /**
* Resets the states and unbinds storage instances for an installation session.
* TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ae88ba5..e0bc764 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9936,6 +9936,11 @@
* @hide */
public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
+ /** Timeout for app integrity verification.
+ * @hide */
+ public static final String APP_INTEGRITY_VERIFICATION_TIMEOUT =
+ "app_integrity_verification_timeout";
+
/** Default response code for package verification.
* @hide */
public static final String PACKAGE_VERIFIER_DEFAULT_RESPONSE = "verifier_default_response";
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 926d8fc..0410c90 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -336,7 +336,7 @@
* an input channel where the client can receive input.
*/
void grantInputChannel(int displayId, in SurfaceControl surface, in IWindow window,
- in IBinder hostInputToken, int flags, out InputChannel outInputChannel);
+ in IBinder hostInputToken, int flags, int type, out InputChannel outInputChannel);
/**
* Update the flags on an input channel associated with a particular surface.
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index dbbe4b6..a480072 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -208,26 +208,6 @@
}
/**
- * Called by {@link ViewRootImpl} to feedback the state of the screen for this view.
- * @param newScreenState The new state of the screen. Can be either
- * {@link View#SCREEN_STATE_ON} or {@link View#SCREEN_STATE_OFF}
- */
- @UiThread
- void onScreenStateChanged(int newScreenState) {
- if (!getImmDelegate().isCurrentRootView(mViewRootImpl)) {
- return;
- }
- // Close input connection and IME when the screen is turn off for security concern.
- if (newScreenState == View.SCREEN_STATE_OFF && mServedView != null) {
- if (DEBUG) {
- Log.d(TAG, "onScreenStateChanged, disconnect input when screen turned off");
- }
- mNextServedView = null;
- mViewRootImpl.dispatchCheckFocus();
- }
- }
-
- /**
* @param windowAttribute {@link WindowManager.LayoutParams} to be checked.
* @return Whether the window is in local focus mode or not.
*/
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 9dfdd06..3215b7c 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -21,6 +21,7 @@
import android.annotation.UiThread;
import android.graphics.Rect;
import android.os.Handler;
+import android.os.Trace;
import android.util.SparseArray;
import android.view.InsetsController.AnimationType;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
@@ -60,6 +61,9 @@
@Override
public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW,
+ "InsetsAsyncAnimation: " + WindowInsets.Type.toString(runner.getTypes()),
+ runner.getTypes());
releaseControls(mControl.getControls());
mMainThreadHandler.post(() ->
mOuterCallbacks.notifyFinished(InsetsAnimationThreadControlRunner.this, shown));
@@ -93,7 +97,11 @@
mOuterCallbacks = controller;
mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
types, mCallbacks, durationMs, interpolator, animationType);
- InsetsAnimationThread.getHandler().post(() -> listener.onReady(mControl, types));
+ InsetsAnimationThread.getHandler().post(() -> {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
+ "InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
+ listener.onReady(mControl, types);
+ });
}
private void releaseControls(SparseArray<InsetsSourceControl> controls) {
@@ -102,15 +110,6 @@
}
}
- private SparseArray<InsetsSourceControl> copyControls(
- SparseArray<InsetsSourceControl> controls) {
- SparseArray<InsetsSourceControl> copy = new SparseArray<>(controls.size());
- for (int i = 0; i < controls.size(); i++) {
- copy.append(controls.keyAt(i), new InsetsSourceControl(controls.valueAt(i)));
- }
- return copy;
- }
-
@Override
@UiThread
public int getTypes() {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 2d17b6d..d12a122 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -35,6 +35,7 @@
import android.graphics.Rect;
import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.Trace;
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
@@ -567,11 +568,15 @@
private void updateState(InsetsState newState) {
mState.setDisplayFrame(newState.getDisplayFrame());
for (int i = newState.getSourcesCount() - 1; i >= 0; i--) {
- InsetsSource source = newState.sourceAt(i);
- getSourceConsumer(source.getType()).updateSource(source);
+ final InsetsSource source = newState.sourceAt(i);
+ final int type = source.getType();
+ final InsetsSourceConsumer consumer = getSourceConsumer(type);
+ consumer.updateSource(source);
+ mHost.updateCompatSysUiVisibility(type, source.isVisible(),
+ consumer.getControl() != null);
}
for (int i = mState.getSourcesCount() - 1; i >= 0; i--) {
- InsetsSource source = mState.sourceAt(i);
+ final InsetsSource source = mState.sourceAt(i);
if (newState.peekSource(source.getType()) == null) {
mState.removeSource(source.getType());
}
@@ -1006,14 +1011,6 @@
}
/**
- * @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean)
- */
- public void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible,
- boolean hasControl) {
- mHost.updateCompatSysUiVisibility(type, visible, hasControl);
- }
-
- /**
* Called when current window gains focus.
*/
public void onWindowFocusGained() {
@@ -1143,6 +1140,8 @@
if (controller.isCancelled()) {
return;
}
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
+ "InsetsAnimation: " + WindowInsets.Type.toString(types), types);
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
RunningAnimation runningAnimation = mRunningAnimations.get(i);
if (runningAnimation.runner == controller) {
@@ -1159,6 +1158,9 @@
@VisibleForTesting
public void dispatchAnimationEnd(WindowInsetsAnimation animation) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW,
+ "InsetsAnimation: " + WindowInsets.Type.toString(animation.getTypeMask()),
+ animation.getTypeMask());
mHost.dispatchWindowInsetsAnimationEnd(animation);
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 2dcfd89..a0cdcfe 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -200,20 +200,12 @@
}
boolean applyLocalVisibilityOverride() {
- InsetsSource source = mState.peekSource(mType);
- final boolean isVisible = source != null && source.isVisible();
- final boolean hasControl = mSourceControl != null;
-
- // We still need to let the legacy app know the visibility change even if we don't have the
- // control.
- mController.updateCompatSysUiVisibility(
- mType, hasControl ? mRequestedVisible : isVisible, hasControl);
// If we don't have control, we are not able to change the visibility.
- if (!hasControl) {
+ if (mSourceControl == null) {
return false;
}
- if (isVisible == mRequestedVisible) {
+ if (mState.getSource(mType).isVisible() == mRequestedVisible) {
return false;
}
mState.getSource(mType).setVisible(mRequestedVisible);
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 7086dc0..3850781 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -275,12 +275,4 @@
// ViewRoot will release mSurfaceControl for us.
mViewRoot.die(false /* immediate */);
}
-
- /**
- * Tell this viewroot to clean itself up.
- * @hide
- */
- public void die() {
- mViewRoot.die(false /* immediate */);
- }
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index db6fe0f..bd811fc 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -134,6 +134,23 @@
// we need to preserve the old one until the new one has drawn.
SurfaceControl mDeferredDestroySurfaceControl;
SurfaceControl mBackgroundControl;
+
+ /**
+ * We use this lock in SOME cases when reading or writing SurfaceControl,
+ * but use the following model so that the RenderThread can run locklessly
+ * in the position up-date case.
+ *
+ * 1. UI Thread can read from mSurfaceControl (use in Transactions) without
+ * holding the lock.
+ * 2. UI Thread will hold the lock when writing to mSurfaceControl (calling release
+ * or remove).
+ * 3. Render thread will also hold the lock when writing to mSurfaceControl (e.g.
+ * calling release from positionLost).
+ * 3. RenderNode.PositionUpdateListener::positionChanged will only be called
+ * when the UI thread is paused (blocked on the Render thread).
+ * 4. positionChanged thus will not be required to hold the lock as the
+ * UI thread is blocked, and the other writer is the RT itself.
+ */
final Object mSurfaceControlLock = new Object();
final Rect mTmpRect = new Rect();
@@ -1297,15 +1314,19 @@
(viewRoot != null ? viewRoot.getBLASTSyncTransaction() : mRtTransaction) :
mRtTransaction;
- if (frameNumber > 0 && viewRoot != null && !useBLAST) {
- if (viewRoot.mSurface.isValid()) {
- mRtTransaction.deferTransactionUntil(mSurfaceControl,
- viewRoot.getRenderSurfaceControl(), frameNumber);
- }
- }
- t.hide(mSurfaceControl);
-
+ /**
+ * positionLost can be called while UI thread is un-paused so we
+ * need to hold the lock here.
+ */
synchronized (mSurfaceControlLock) {
+ if (frameNumber > 0 && viewRoot != null && !useBLAST) {
+ if (viewRoot.mSurface.isValid()) {
+ mRtTransaction.deferTransactionUntil(mSurfaceControl,
+ viewRoot.getRenderSurfaceControl(), frameNumber);
+ }
+ }
+ t.hide(mSurfaceControl);
+
if (mRtReleaseSurfaces) {
mRtReleaseSurfaces = false;
mRtTransaction.remove(mSurfaceControl);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5b9cd77..d928356 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1496,7 +1496,6 @@
final int newScreenState = toViewScreenState(newDisplayState);
if (oldScreenState != newScreenState) {
mView.dispatchScreenStateChanged(newScreenState);
- mImeFocusController.onScreenStateChanged(newScreenState);
}
if (oldDisplayState == Display.STATE_OFF) {
// Draw was suppressed so we need to for it to happen here.
@@ -1977,6 +1976,10 @@
mCompatibleVisibilityInfo.globalVisibility =
(mCompatibleVisibilityInfo.globalVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE)
| (mAttachInfo.mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ if (mDispatchedSystemUiVisibility != mCompatibleVisibilityInfo.globalVisibility) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MSG_DISPATCH_SYSTEM_UI_VISIBILITY, mCompatibleVisibilityInfo));
+ }
if (mAttachInfo.mKeepScreenOn != oldScreenOn
|| mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
|| mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
@@ -2030,7 +2033,7 @@
info.globalVisibility |= systemUiFlag;
}
if (mDispatchedSystemUiVisibility != info.globalVisibility) {
- scheduleTraversals();
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, info));
}
}
@@ -2478,9 +2481,6 @@
mAttachInfo.mForceReportNewAttributes = false;
params = lp;
}
- if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- handleDispatchSystemUiVisibilityChanged(mCompatibleVisibilityInfo);
- }
if (mFirst || mAttachInfo.mViewVisibilityChanged) {
mAttachInfo.mViewVisibilityChanged = false;
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index aad1c60..4d6b72f 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -1328,30 +1328,36 @@
}
}
- static String toString(@InsetsType int type) {
- switch (type) {
- case STATUS_BARS:
- return "statusBars";
- case NAVIGATION_BARS:
- return "navigationBars";
- case CAPTION_BAR:
- return "captionBar";
- case IME:
- return "ime";
- case SYSTEM_GESTURES:
- return "systemGestures";
- case MANDATORY_SYSTEM_GESTURES:
- return "mandatorySystemGestures";
- case TAPPABLE_ELEMENT:
- return "tappableElement";
- case DISPLAY_CUTOUT:
- return "displayCutout";
- case WINDOW_DECOR:
- return "windowDecor";
- default:
- throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST,"
- + " type=" + type);
+ static String toString(@InsetsType int types) {
+ StringBuilder result = new StringBuilder();
+ if ((types & STATUS_BARS) != 0) {
+ result.append("statusBars |");
}
+ if ((types & NAVIGATION_BARS) != 0) {
+ result.append("navigationBars |");
+ }
+ if ((types & IME) != 0) {
+ result.append("ime |");
+ }
+ if ((types & SYSTEM_GESTURES) != 0) {
+ result.append("systemGestures |");
+ }
+ if ((types & MANDATORY_SYSTEM_GESTURES) != 0) {
+ result.append("mandatorySystemGestures |");
+ }
+ if ((types & TAPPABLE_ELEMENT) != 0) {
+ result.append("tappableElement |");
+ }
+ if ((types & DISPLAY_CUTOUT) != 0) {
+ result.append("displayCutout |");
+ }
+ if ((types & WINDOW_DECOR) != 0) {
+ result.append("windowDecor |");
+ }
+ if (result.length() > 0) {
+ result.delete(result.length() - 2, result.length());
+ }
+ return result.toString();
}
private Type() {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8cb05da..7607127 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1162,7 +1162,7 @@
/**
* Window type: shows directly above the keyguard. The layer is
- * reserved for screenshot region selection. These windows must not take input focus.
+ * reserved for screenshot animation, region selection and UI.
* In multiuser systems shows only on the owning user's window.
* @hide
*/
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index cd954c4..d20ffb3 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -143,7 +143,7 @@
WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) {
try {
mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
- outInputChannel);
+ attrs.type, outInputChannel);
} catch (RemoteException e) {
Log.e(TAG, "Failed to grant input to surface: ", e);
}
@@ -432,7 +432,7 @@
@Override
public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
- IBinder hostInputToken, int flags, InputChannel outInputChannel) {
+ IBinder hostInputToken, int flags, int type, InputChannel outInputChannel) {
}
@Override
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index ca5c417..813234f 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -752,7 +752,10 @@
}
}
- private static String typeToString(int type) {
+ /**
+ * @hide
+ */
+ public static String typeToString(int type) {
switch (type) {
case TYPE_APPLICATION: {
return "TYPE_APPLICATION";
@@ -770,7 +773,7 @@
return "TYPE_SPLIT_SCREEN_DIVIDER";
}
default:
- return "<UNKNOWN>";
+ return "<UNKNOWN:" + type + ">";
}
}
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index 4c72474..e4ac588 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -326,11 +326,13 @@
@MainThread
private void handleOnSurfacePackageReleased() {
- mSurfacePackage = null;
- try {
- mInlineContentProvider.onSurfacePackageReleased();
- } catch (RemoteException e) {
- Slog.w(TAG, "Error calling onSurfacePackageReleased(): " + e);
+ if (mSurfacePackage != null) {
+ try {
+ mInlineContentProvider.onSurfacePackageReleased();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error calling onSurfacePackageReleased(): " + e);
+ }
+ mSurfacePackage = null;
}
}
@@ -573,7 +575,7 @@
};
@DataClass.Generated(
- time = 1588308946517L,
+ time = 1589396017700L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate static boolean isValid(int,int,int)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index f8522ed..f78ec7c 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -189,14 +189,29 @@
public void onCloseWindow(WebView window) {}
/**
- * Tell the client to display a javascript alert dialog. If the client
- * returns {@code true}, WebView will assume that the client will handle the
- * dialog. If the client returns {@code false}, it will continue execution.
+ * Notify the host application that the web page wants to display a
+ * JavaScript {@code alert()} dialog.
+ * <p>The default behavior if this method returns {@code false} or is not
+ * overridden is to show a dialog containing the alert message and suspend
+ * JavaScript execution until the dialog is dismissed.
+ * <p>To show a custom dialog, the app should return {@code true} from this
+ * method, in which case the default dialog will not be shown and JavaScript
+ * execution will be suspended. The app should call
+ * {@code JsResult.confirm()} when the custom dialog is dismissed such that
+ * JavaScript execution can be resumed.
+ * <p>To suppress the dialog and allow JavaScript execution to
+ * continue, call {@code JsResult.confirm()} immediately and then return
+ * {@code true}.
+ * <p>Note that if the {@link WebChromeClient} is {@code null}, the default
+ * dialog will be suppressed and Javascript execution will continue
+ * immediately.
+ *
* @param view The WebView that initiated the callback.
* @param url The url of the page requesting the dialog.
* @param message Message to be displayed in the window.
- * @param result A JsResult to confirm that the user hit enter.
- * @return boolean Whether the client will handle the alert dialog.
+ * @param result A JsResult to confirm that the user closed the window.
+ * @return boolean {@code true} if the request is handled or ignored.
+ * {@code false} if WebView needs to show the default dialog.
*/
public boolean onJsAlert(WebView view, String url, String message,
JsResult result) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 29914aa..226f5797 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8315,23 +8315,6 @@
return false;
}
- /**
- * Returns true if pressing TAB in this field advances focus instead
- * of inserting the character. Insert tabs only in multi-line editors.
- */
- private boolean shouldAdvanceFocusOnTab() {
- if (getKeyListener() != null && !mSingleLine && mEditor != null
- && (mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS)
- == EditorInfo.TYPE_CLASS_TEXT) {
- int multilineFlags = EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE
- | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
- if ((mEditor.mInputType & multilineFlags) != 0) {
- return false;
- }
- }
- return true;
- }
-
private boolean isDirectionalNavigationKey(int keyCode) {
switch(keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
@@ -8400,9 +8383,8 @@
case KeyEvent.KEYCODE_TAB:
if (event.hasNoModifiers() || event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
- if (shouldAdvanceFocusOnTab()) {
- return KEY_EVENT_NOT_HANDLED;
- }
+ // Tab is used to move focus.
+ return KEY_EVENT_NOT_HANDLED;
}
break;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 9c2df13..3084f2a 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -358,6 +358,7 @@
: isHttpSchemeAndViewAction(getTargetIntent());
mSupportsAlwaysUseOption = supportsAlwaysUseOption;
+ mWorkProfileUserHandle = fetchWorkProfileUserProfile();
// The last argument of createResolverListAdapter is whether to do special handling
// of the last used choice to highlight it in the list. We need to always
@@ -366,7 +367,6 @@
// to handle. We also turn it off when the work tab is shown to simplify the UX.
boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction()
&& !shouldShowTabs();
- mWorkProfileUserHandle = fetchWorkProfileUserProfile();
mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
if (configureContentView()) {
return;
@@ -1612,6 +1612,7 @@
}
private void setupProfileTabs() {
+ maybeHideDivider();
TabHost tabHost = findViewById(R.id.profile_tabhost);
tabHost.setup();
ViewPager viewPager = findViewById(R.id.profile_pager);
@@ -1660,6 +1661,17 @@
findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE);
}
+ private void maybeHideDivider() {
+ if (!isIntentPicker()) {
+ return;
+ }
+ final View divider = findViewById(R.id.divider);
+ if (divider == null) {
+ return;
+ }
+ divider.setVisibility(View.GONE);
+ }
+
/**
* Callback called when user changes the profile tab.
* <p>This method is intended to be overridden by subclasses.
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 49c9302..ad6c7e8 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -132,6 +132,7 @@
}
};
}
+
private static final String TAG = "ScreenshotHelper";
// Time until we give up on the screenshot & show an error instead.
@@ -146,8 +147,6 @@
mContext = context;
}
-
-
/**
* Request a screenshot be taken.
*
@@ -284,8 +283,8 @@
break;
case SCREENSHOT_MSG_PROCESS_COMPLETE:
synchronized (mScreenshotLock) {
- if (mScreenshotConnection == myConn) {
- mContext.unbindService(mScreenshotConnection);
+ if (myConn != null && mScreenshotConnection == myConn) {
+ mContext.unbindService(myConn);
mScreenshotConnection = null;
mScreenshotService = null;
}
diff --git a/core/proto/android/app/tvsettings_enums.proto b/core/proto/android/app/tvsettings_enums.proto
index 30d365c..31c5dd6 100644
--- a/core/proto/android/app/tvsettings_enums.proto
+++ b/core/proto/android/app/tvsettings_enums.proto
@@ -44,6 +44,24 @@
/** Denotes that a toggle is clicked by a user. */
TOGGLE_INTERACTED = 3;
+ /**
+ * Denotes that a TvSettings page is being focused in the forward direction
+ * into the settings tree.
+ */
+ PAGE_FOCUSED_FORWARD = 4;
+
+ /**
+ * Denotes that a TvSettings page is being focused in the backward direction
+ * up the settings tree.
+ */
+ PAGE_FOCUSED_BACKWARD = 5;
+
+ /** Denotes that a toggle is turned on by a user. */
+ TOGGLED_ON = 6;
+
+ /** Denotes that a toggle is turned off by a user. */
+ TOGGLED_OFF = 7;
+
}
/**
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index fe11769..196a0e8 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -59,7 +59,7 @@
<Button
android:id="@+id/resolver_empty_state_button"
android:layout_below="@+id/resolver_empty_state_subtitle"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="8dp"
android:text="@string/resolver_switch_on_work"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f3f3d47d..54d14f8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1475,7 +1475,7 @@
<string name="permdesc_mediaLocation">Allows the app to read locations from your media collection.</string>
<!-- Title shown when the system-provided biometric dialog is shown, asking the user to authenticate. [CHAR LIMIT=40] -->
- <string name="biometric_dialog_default_title">Verify it\u2018s you</string>
+ <string name="biometric_dialog_default_title">Verify it\u2019s you</string>
<!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] -->
<string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string>
<!-- Message shown when biometric authentication was canceled by the user [CHAR LIMIT=50] -->
@@ -3595,31 +3595,37 @@
<!-- Notification body when new external media is detected [CHAR LIMIT=30] -->
<string name="ext_media_new_notification_title">New <xliff:g id="name" example="SD card">%s</xliff:g></string>
+ <!-- Automotive specific notification body when new external media is detected [CHAR LIMIT=30] -->
+ <string name="ext_media_new_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
<!-- Notification body when new external media is detected [CHAR LIMIT=NONE] -->
<string name="ext_media_new_notification_message">Tap to set up</string>
- <!-- Automotive specific notification body when new external media is detected. Empty because there is no fix action (b/151671685) [CHAR LIMIT=NONE] -->
- <string name="ext_media_new_notification_message" product="automotive"></string>
+ <!-- Automotive specific notification body when new external media is detected. [CHAR LIMIT=NONE] -->
+ <string name="ext_media_new_notification_message" product="automotive">You may need to reformat the device. Tap to eject.</string>
<!-- Notification body when external media is ready for use [CHAR LIMIT=NONE] -->
<string name="ext_media_ready_notification_message">For transferring photos and media</string>
<!-- Notification title when external media is unmountable (corrupt) [CHAR LIMIT=30] -->
<string name="ext_media_unmountable_notification_title">Issue with <xliff:g id="name" example="SD card">%s</xliff:g></string>
+ <!-- Automotive specific notification title when external media is unmountable (corrupt) [CHAR LIMIT=30] -->
+ <string name="ext_media_unmountable_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
<!-- Notification body when external media is unmountable (corrupt) [CHAR LIMIT=NONE] -->
<string name="ext_media_unmountable_notification_message">Tap to fix</string>
<!-- TV-specific notification body when external media is unmountable (corrupt) [CHAR LIMIT=NONE] -->
<string name="ext_media_unmountable_notification_message" product="tv"><xliff:g id="name" example="SD card">%s</xliff:g> is corrupt. Select to fix.</string>
- <!-- Automotive specific notification body when external media is unmountable (corrupt). Empty because there is no fix action (b/151671685) [CHAR LIMIT=NONE] -->
- <string name="ext_media_unmountable_notification_message" product="automotive"></string>
+ <!-- Automotive specific notification body when external media is unmountable (corrupt) [CHAR LIMIT=NONE] -->
+ <string name="ext_media_unmountable_notification_message" product="automotive">You may need to reformat the device. Tap to eject.</string>
<!-- Notification title when external media is unsupported [CHAR LIMIT=30] -->
<string name="ext_media_unsupported_notification_title">Unsupported <xliff:g id="name" example="SD card">%s</xliff:g></string>
+ <!-- Automotive specific notification title when external media is unsupported [CHAR LIMIT=30] -->
+ <string name="ext_media_unsupported_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
<!-- Notification body when external media is unsupported [CHAR LIMIT=NONE] -->
<string name="ext_media_unsupported_notification_message">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Tap to set up in a supported format.</string>
<!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
<string name="ext_media_unsupported_notification_message" product="tv">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Select to set up in a supported format.</string>
- <!-- Automotive specific notification body when external media is unsupported. No action is specified to fix (b/151671685) [CHAR LIMIT=NONE] -->
- <string name="ext_media_unsupported_notification_message" product="automotive">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>.</string>
+ <!-- Automotive specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
+ <string name="ext_media_unsupported_notification_message" product="automotive">You may need to reformat the device</string>
<!-- Notification title when external media is unsafely removed [CHAR LIMIT=30] -->
<string name="ext_media_badremoval_notification_title"><xliff:g id="name" example="SD card">%s</xliff:g> unexpectedly removed</string>
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 5f12bf0..8eca650 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -26,11 +26,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -98,8 +95,6 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- doNothing().when(mMockController).updateCompatSysUiVisibility(
- anyInt(), anyBoolean(), anyBoolean());
mTopLeash = new SurfaceControl.Builder(mSession)
.setName("testSurface")
.build();
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index ecc3b4f..5c16772 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -28,29 +28,23 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static org.hamcrest.Matchers.equalTo;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.assertTrue;
import android.content.Context;
-import android.graphics.Insets;
-import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
/**
* Tests for {@link ViewRootImpl}
*
@@ -62,59 +56,18 @@
@RunWith(AndroidJUnit4.class)
public class ViewRootImplTest {
- private Context mContext;
- private ViewRootImplAccessor mViewRootImpl;
+ private ViewRootImpl mViewRootImpl;
@Before
public void setUp() throws Exception {
- mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final Context context = getInstrumentation().getTargetContext();
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- mViewRootImpl = new ViewRootImplAccessor(
- new ViewRootImpl(mContext, mContext.getDisplayNoVerify()));
- });
- }
-
- @Test
- public void negativeInsets_areSetToZero() throws Exception {
- assumeTrue(ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- mViewRootImpl.getAttachInfo().getContentInsets().set(-10, -20, -30 , -40);
- mViewRootImpl.getAttachInfo().getStableInsets().set(-10, -20, -30 , -40);
- final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
-
- assertThat(insets.getSystemWindowInsets(), equalTo(Insets.NONE));
- assertThat(insets.getStableInsets(), equalTo(Insets.NONE));
- }
-
- @Test
- public void negativeInsets_areSetToZero_positiveAreLeftAsIs() throws Exception {
- assumeTrue(ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- mViewRootImpl.getAttachInfo().getContentInsets().set(-10, 20, -30 , 40);
- mViewRootImpl.getAttachInfo().getStableInsets().set(10, -20, 30 , -40);
- final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
-
- assertThat(insets.getSystemWindowInsets(), equalTo(Insets.of(0, 20, 0, 40)));
- assertThat(insets.getStableInsets(), equalTo(Insets.of(10, 0, 30, 0)));
- }
-
- @Test
- public void positiveInsets_areLeftAsIs() throws Exception {
- assumeTrue(ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- mViewRootImpl.getAttachInfo().getContentInsets().set(10, 20, 30 , 40);
- mViewRootImpl.getAttachInfo().getStableInsets().set(10, 20, 30 , 40);
- final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
-
- assertThat(insets.getSystemWindowInsets(), equalTo(Insets.of(10, 20, 30, 40)));
- assertThat(insets.getStableInsets(), equalTo(Insets.of(10, 20, 30, 40)));
+ getInstrumentation().runOnMainSync(() ->
+ mViewRootImpl = new ViewRootImpl(context, context.getDisplayNoVerify()));
}
@Test
public void adjustLayoutParamsForCompatibility_layoutFullscreen() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
attrs.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
@@ -125,8 +78,6 @@
@Test
public void adjustLayoutParamsForCompatibility_layoutInScreen() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
attrs.flags = FLAG_LAYOUT_IN_SCREEN;
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
@@ -137,8 +88,6 @@
@Test
public void adjustLayoutParamsForCompatibility_layoutHideNavigation() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
attrs.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
@@ -149,28 +98,22 @@
@Test
public void adjustLayoutParamsForCompatibility_toast() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_TOAST);
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
- assertEquals(true, attrs.isFitInsetsIgnoringVisibility());
+ assertTrue(attrs.isFitInsetsIgnoringVisibility());
}
@Test
public void adjustLayoutParamsForCompatibility_systemAlert() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_SYSTEM_ALERT);
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
- assertEquals(true, attrs.isFitInsetsIgnoringVisibility());
+ assertTrue(attrs.isFitInsetsIgnoringVisibility());
}
@Test
public void adjustLayoutParamsForCompatibility_fitSystemBars() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
@@ -180,8 +123,6 @@
@Test
public void adjustLayoutParamsForCompatibility_noAdjustLayout() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
final int types = Type.all();
final int sides = Side.TOP | Side.LEFT;
@@ -201,11 +142,8 @@
@Test
public void adjustLayoutParamsForCompatibility_noAdjustAppearance() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- final ViewRootImpl viewRoot = mViewRootImpl.get();
- final WindowInsetsController controller = viewRoot.getInsetsController();
- final WindowManager.LayoutParams attrs = viewRoot.mWindowAttributes;
+ final WindowInsetsController controller = mViewRootImpl.getInsetsController();
+ final WindowManager.LayoutParams attrs = mViewRootImpl.mWindowAttributes;
final int appearance = 0;
controller.setSystemBarsAppearance(appearance, 0xffffffff);
attrs.systemUiVisibility = SYSTEM_UI_FLAG_LOW_PROFILE
@@ -220,11 +158,8 @@
@Test
public void adjustLayoutParamsForCompatibility_noAdjustBehavior() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- final ViewRootImpl viewRoot = mViewRootImpl.get();
- final WindowInsetsController controller = viewRoot.getInsetsController();
- final WindowManager.LayoutParams attrs = viewRoot.mWindowAttributes;
+ final WindowInsetsController controller = mViewRootImpl.getInsetsController();
+ final WindowManager.LayoutParams attrs = mViewRootImpl.mWindowAttributes;
final int behavior = BEHAVIOR_SHOW_BARS_BY_TOUCH;
controller.setSystemBarsBehavior(behavior);
attrs.systemUiVisibility = SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
@@ -234,59 +169,4 @@
// setSystemBarsBehavior.
assertEquals(behavior, controller.getSystemBarsBehavior());
}
-
- private static class ViewRootImplAccessor {
-
- private final ViewRootImpl mViewRootImpl;
-
- ViewRootImplAccessor(ViewRootImpl viewRootImpl) {
- mViewRootImpl = viewRootImpl;
- }
-
- public ViewRootImpl get() {
- return mViewRootImpl;
- }
-
- AttachInfoAccessor getAttachInfo() throws Exception {
- return new AttachInfoAccessor(
- getField(mViewRootImpl, ViewRootImpl.class.getDeclaredField("mAttachInfo")));
- }
-
- WindowInsets getWindowInsets(boolean forceConstruct) throws Exception {
- return (WindowInsets) invokeMethod(mViewRootImpl,
- ViewRootImpl.class.getDeclaredMethod("getWindowInsets", boolean.class),
- forceConstruct);
- }
-
- class AttachInfoAccessor {
-
- private final Class<?> mClass;
- private final Object mAttachInfo;
-
- AttachInfoAccessor(Object attachInfo) throws Exception {
- mAttachInfo = attachInfo;
- mClass = ViewRootImpl.class.getClassLoader().loadClass(
- "android.view.View$AttachInfo");
- }
-
- Rect getContentInsets() throws Exception {
- return (Rect) getField(mAttachInfo, mClass.getDeclaredField("mContentInsets"));
- }
-
- Rect getStableInsets() throws Exception {
- return (Rect) getField(mAttachInfo, mClass.getDeclaredField("mStableInsets"));
- }
- }
-
- private static Object getField(Object o, Field field) throws Exception {
- field.setAccessible(true);
- return field.get(o);
- }
-
- private static Object invokeMethod(Object o, Method method, Object... args)
- throws Exception {
- method.setAccessible(true);
- return method.invoke(o, args);
- }
- }
}
diff --git a/core/tests/overlaytests/host/Android.bp b/core/tests/overlaytests/host/Android.bp
index 2b38cca..a2fcef5 100644
--- a/core/tests/overlaytests/host/Android.bp
+++ b/core/tests/overlaytests/host/Android.bp
@@ -16,7 +16,7 @@
name: "OverlayHostTests",
srcs: ["src/**/*.java"],
libs: ["tradefed"],
- test_suites: ["device-tests"],
+ test_suites: ["general-tests"],
target_required: [
"OverlayHostTests_NonPlatformSignatureOverlay",
"OverlayHostTests_PlatformSignatureStaticOverlay",
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 0389639..bdb6bcc 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -324,6 +324,7 @@
<permission name="android.permission.MANAGE_ROLLBACKS"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
+ <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/>
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java
index c4f11a0..c61f1ed 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java
@@ -30,6 +30,8 @@
import android.view.DisplayInfo;
import android.view.Surface;
+import androidx.annotation.Nullable;
+
/**
* Toolkit class for calculation of the display feature bounds within the window.
* NOTE: This sample implementation only works for Activity windows, because there is no public APIs
@@ -84,7 +86,7 @@
/** Transform rectangle from absolute coordinate space to the window coordinate space. */
static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) {
- Rect windowRect = getWindowRect(windowToken);
+ Rect windowRect = getWindowBounds(windowToken);
if (windowRect == null) {
inOutRect.setEmpty();
return;
@@ -101,13 +103,12 @@
* Get the current window bounds in absolute coordinates.
* NOTE: Only works with Activity windows.
*/
- private static Rect getWindowRect(IBinder windowToken) {
+ @Nullable
+ private static Rect getWindowBounds(IBinder windowToken) {
Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
- final Rect windowRect = new Rect();
- if (activity != null) {
- activity.getWindow().getDecorView().getWindowDisplayFrame(windowRect);
- }
- return windowRect;
+ return activity != null
+ ? activity.getWindowManager().getCurrentWindowMetrics().getBounds()
+ : null;
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java b/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java
index 2bdb85f..03b61e0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java
@@ -52,6 +52,15 @@
new CarAudioManager.CarVolumeCallback() {
@Override
public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
+ initVolumeDialogComponent();
+ }
+
+ @Override
+ public void onMasterMuteChanged(int zoneId, int flags) {
+ initVolumeDialogComponent();
+ }
+
+ private void initVolumeDialogComponent() {
if (mVolumeDialogComponent == null) {
mMainHandler.post(() -> {
mVolumeDialogComponent = mVolumeDialogComponentLazy.get();
@@ -60,11 +69,6 @@
mCarAudioManager.unregisterCarVolumeCallback(mVolumeChangeCallback);
}
}
-
- @Override
- public void onMasterMuteChanged(int zoneId, int flags) {
- // ignored
- }
};
private boolean mEnabled;
diff --git a/packages/InputDevices/res/raw/keyboard_layout_croatian_and_slovenian.kcm b/packages/InputDevices/res/raw/keyboard_layout_croatian_and_slovenian.kcm
index eec9d27..96445a4 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_croatian_and_slovenian.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_croatian_and_slovenian.kcm
@@ -258,9 +258,9 @@
}
key SEMICOLON {
- label: '\u010d'
- base: '\u010c'
- shift, capslock: '\u010d'
+ label: '\u010c'
+ base: '\u010d'
+ shift, capslock: '\u010c'
}
key APOSTROPHE {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 1d06df0..008a433 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -475,5 +475,10 @@
public void onRequestFailed(int reason) {
dispatchOnRequestFailed(reason);
}
+
+ @Override
+ public void onSessionUpdated(RoutingSessionInfo sessionInfo) {
+ dispatchDataChanged();
+ }
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 99c568a..734866f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -625,6 +625,15 @@
}
@Test
+ public void onSessionUpdated_shouldDispatchDataChanged() {
+ mInfoMediaManager.registerCallback(mCallback);
+
+ mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(mock(RoutingSessionInfo.class));
+
+ verify(mCallback).onDeviceAttributesChanged();
+ }
+
+ @Test
public void addMediaDevice_verifyDeviceTypeCanCorrespondToMediaDevice() {
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 29c31ea..e537b76 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -588,8 +588,9 @@
Settings.Global.POWER_BUTTON_VERY_LONG_PRESS,
Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, // Temporary for R beta
Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
- Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT,
- Settings.Global.CACHED_APPS_FREEZER_ENABLED);
+ Settings.Global.CACHED_APPS_FREEZER_ENABLED,
+ Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
+ Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT);
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 96caf31..83d7218 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -233,6 +233,9 @@
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
<uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
+ <!-- Permission required for CTS test - BatterySaverTest -->
+ <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
+
<!-- Permission required for CTS test - UiModeManagerTest -->
<uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 61b1e30..c4ea2e9 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -259,6 +259,9 @@
<!-- Permission to register process observer -->
<uses-permission android:name="android.permission.SET_ACTIVITY_WATCHER"/>
+ <!-- Restore settings (used by QS) even if they have been modified -->
+ <uses-permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE" />
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/res/drawable/control_no_favorites_background.xml b/packages/SystemUI/res/drawable/control_no_favorites_background.xml
index 947c77b..d895dd0 100644
--- a/packages/SystemUI/res/drawable/control_no_favorites_background.xml
+++ b/packages/SystemUI/res/drawable/control_no_favorites_background.xml
@@ -16,7 +16,22 @@
* limitations under the License.
*/
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <stroke android:width="1dp" android:color="@*android:color/foreground_material_dark"/>
- <corners android:radius="@dimen/control_corner_radius" />
-</shape>
+<!-- Should be kept in sync with the wallet plugin, as both share a similar
+ design: packages/apps/QuickAccessWallet -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="#000000" />
+ <corners android:radius="@dimen/control_corner_radius" />
+ </shape>
+ </item>
+ <item>
+ <shape>
+ <stroke
+ android:width="1dp"
+ android:color="#4DFFFFFF" />
+ <corners android:radius="@dimen/control_corner_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/bubble_overflow_view.xml b/packages/SystemUI/res/layout/bubble_overflow_view.xml
index 1ed1f07..1218fba 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_view.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_view.xml
@@ -30,9 +30,8 @@
<TextView
android:id="@+id/bubble_view_name"
- android:fontFamily="@*android:string/config_bodyFontFamily"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"
- android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="13sp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLines="1"
diff --git a/packages/SystemUI/res/layout/controls_icon.xml b/packages/SystemUI/res/layout/controls_icon.xml
index cc46ced..12bc5f6 100644
--- a/packages/SystemUI/res/layout/controls_icon.xml
+++ b/packages/SystemUI/res/layout/controls_icon.xml
@@ -19,8 +19,8 @@
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="28dp"
- android:layout_height="28dp"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
android:scaleType="fitCenter"
- android:layout_marginLeft="2dp"
- android:layout_marginRight="2dp" />
+ android:layout_marginLeft="5dp"
+ android:layout_marginRight="5dp" />
diff --git a/packages/SystemUI/res/layout/controls_no_favorites.xml b/packages/SystemUI/res/layout/controls_no_favorites.xml
index 4128230..1b24ee9 100644
--- a/packages/SystemUI/res/layout/controls_no_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_no_favorites.xml
@@ -24,11 +24,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:paddingTop="40dp"
- android:paddingBottom="40dp"
- android:layout_marginLeft="10dp"
- android:layout_marginRight="10dp"
- android:layout_marginTop="@dimen/controls_top_margin"
+ android:paddingVertical="@dimen/controls_setup_vertical_padding"
+ android:layout_marginLeft="@dimen/global_actions_side_margin"
+ android:layout_marginRight="@dimen/global_actions_side_margin"
+ android:layout_marginTop="@dimen/controls_setup_top_margin"
android:background="@drawable/control_no_favorites_background">
<LinearLayout
@@ -37,7 +36,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal"
- android:paddingBottom="8dp" />
+ android:layout_marginBottom="16dp" />
<TextView
style="@style/TextAppearance.ControlSetup.Title"
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index 2401dfb..a261114 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -144,25 +144,36 @@
android:clipToPadding="false"
android:orientation="vertical">
- <LinearLayout
+ <com.android.systemui.statusbar.notification.row.ButtonLinearLayout
+ android:id="@+id/settings_link"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="horizontal">
- <ImageView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:contentDescription="@null"
- android:src="@drawable/ic_info"
- android:tint="?android:attr/textColorPrimary"
- android:layout_marginEnd="8dp"/>
- <TextView
- android:id="@+id/non_configurable_text"
+ android:padding="@dimen/notification_importance_button_padding"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="@drawable/notification_guts_priority_button_bg"
+ android:orientation="vertical">
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@style/TextAppearance.NotificationImportanceChannelGroup" />
- </LinearLayout>
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="horizontal">
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_info"
+ android:tint="?android:attr/textColorPrimary"
+ android:layout_marginEnd="8dp"/>
+ <TextView
+ android:id="@+id/non_configurable_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.NotificationImportanceChannelGroup" />
+ </LinearLayout>
+ </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
<RelativeLayout
android:id="@+id/bottom_buttons"
diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
index 9ad380d..bf06242 100644
--- a/packages/SystemUI/res/layout/qs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -49,6 +49,7 @@
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textColor="@color/media_primary_text"
android:gravity="left"
android:textSize="14sp" />
@@ -58,6 +59,7 @@
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textColor="@color/media_primary_text"
android:gravity="right"
android:textSize="14sp" />
</FrameLayout>
@@ -116,6 +118,7 @@
android:layout_width="@dimen/qs_seamless_icon_size"
android:layout_height="@dimen/qs_seamless_icon_size"
android:layout_marginRight="8dp"
+ android:tint="@color/media_primary_text"
android:src="@*android:drawable/ic_media_seamless" />
<TextView
@@ -125,6 +128,7 @@
android:fontFamily="@*android:string/config_bodyFontFamily"
android:singleLine="true"
android:text="@*android:string/ext_media_seamless_action"
+ android:textColor="@color/media_primary_text"
android:textSize="14sp" />
</LinearLayout>
@@ -138,11 +142,15 @@
android:maxHeight="3dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
+ android:thumbTint="@color/media_primary_text"
+ android:progressTint="@color/media_seekbar_progress"
+ android:progressBackgroundTint="@color/media_disabled"
android:splitTrack="false" />
<!-- App name -->
<TextView
android:id="@+id/app_name"
+ android:textColor="@color/media_primary_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:singleLine="true"
@@ -155,6 +163,7 @@
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:singleLine="true"
+ android:textColor="@color/media_primary_text"
android:textSize="18sp" />
<!-- Artist name -->
@@ -164,10 +173,12 @@
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_bodyFontFamily"
android:singleLine="true"
+ android:textColor="@color/media_primary_text"
android:textSize="14sp" />
<com.android.internal.widget.CachingIconView
android:id="@+id/icon"
+ android:tint="@color/media_primary_text"
android:layout_width="16dp"
android:layout_height="16dp" />
diff --git a/packages/SystemUI/res/layout/qs_media_panel_options.xml b/packages/SystemUI/res/layout/qs_media_panel_options.xml
index 46655e7..e72c0e8 100644
--- a/packages/SystemUI/res/layout/qs_media_panel_options.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel_options.xml
@@ -36,6 +36,7 @@
android:layout_height="18dp"
android:id="@+id/remove_icon"
android:layout_marginEnd="16dp"
+ android:tint="@color/media_primary_text"
android:src="@drawable/ic_clear"/>
<TextView
android:layout_width="wrap_content"
@@ -43,6 +44,7 @@
android:id="@+id/remove_text"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:singleLine="true"
+ android:textColor="@color/media_primary_text"
android:text="@string/controls_media_close_session" />
</LinearLayout>
<TextView
@@ -54,5 +56,6 @@
android:layout_gravity="end|bottom"
android:gravity="bottom"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textColor="@android:color/white"
android:text="@string/cancel" />
</LinearLayout>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 88a7c73..837627c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -239,6 +239,11 @@
<color name="magnification_border_color">#FF9900</color>
+ <!-- media -->
+ <color name="media_primary_text">@android:color/white</color>
+ <color name="media_seekbar_progress">#c0ffffff</color>
+ <color name="media_disabled">#80ffffff</color>
+
<!-- controls -->
<color name="control_primary_text">#E6FFFFFF</color>
<color name="control_secondary_text">#99FFFFFF</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c2694aa..a2e11a7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1281,6 +1281,10 @@
<dimen name="control_status_padding">3dp</dimen>
<fraction name="controls_toggle_bg_intensity">5%</fraction>
<fraction name="controls_dimmed_alpha">40%</fraction>
+ <dimen name="controls_setup_top_margin">16dp</dimen>
+ <dimen name="controls_setup_title">22sp</dimen>
+ <dimen name="controls_setup_subtitle">14sp</dimen>
+ <dimen name="controls_setup_vertical_padding">52dp</dimen>
<!-- Home Controls activity view detail panel-->
<dimen name="controls_activity_view_top_offset">100dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ec29622..8c10f61 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2698,6 +2698,20 @@
<item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> controls added.</item>
</plurals>
+ <!-- Removed control in management screen [CHAR LIMIT=20] -->
+ <string name="controls_removed">Removed</string>
+
+ <!-- a11y state description for a control that is currently favorited [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_favorite">Favorited</string>
+ <!-- a11y state description for a control that is currently favorited with its position [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_favorite_position">Favorited, position <xliff:g id="number" example="1">%d</xliff:g></string>
+ <!-- a11y state description for a control that is currently not favorited [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_not_favorite">Unfavorited</string>
+ <!-- a11y action to favorite a control. It will read as "Double-tap to favorite" in screen readers [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_change_favorite">favorite</string>
+ <!-- a11y action to unfavorite a control. It will read as "Double-tap to unfavorite" in screen readers [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_change_unfavorite">unfavorite</string>
+
<!-- Controls management controls screen default title [CHAR LIMIT=30] -->
<string name="controls_favorite_default_title">Controls</string>
<!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f0edd63..b461295 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -622,6 +622,7 @@
<style name="MediaPlayer.Button" parent="@android:style/Widget.Material.Button.Borderless.Small">
<item name="android:background">@null</item>
+ <item name="android:tint">@android:color/white</item>
</style>
<!-- Used to style charging animation AVD animation -->
@@ -765,11 +766,11 @@
</style>
<style name="TextAppearance.ControlSetup.Title">
- <item name="android:textSize">25sp</item>
+ <item name="android:textSize">@dimen/controls_setup_title</item>
</style>
<style name="TextAppearance.ControlSetup.Subtitle">
- <item name="android:textSize">16sp</item>
+ <item name="android:textSize">@dimen/controls_setup_subtitle</item>
</style>
<!-- The attributes used for title (textAppearanceLarge) and message (textAppearanceMedium)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
index 8bd7c79..30156a0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
@@ -58,7 +58,7 @@
*/
public void onReceive(Context context, Bundle bundle, View view, Size viewSize) {
if (mSurfaceControlViewHost != null) {
- mSurfaceControlViewHost.die();
+ mSurfaceControlViewHost.release();
}
SurfaceControl surfaceControl = SurfaceViewRequestUtils.getSurfaceControl(bundle);
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index 0106609..b1e1434 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -19,8 +19,6 @@
import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
import static android.telephony.PhoneStateListener.LISTEN_NONE;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
-
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -47,6 +45,7 @@
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
@@ -61,9 +60,11 @@
private final boolean mIsEmergencyCallCapable;
private final Handler mMainHandler;
+ private final Handler mBgHandler;
private boolean mTelephonyCapable;
private boolean mShowMissingSim;
private boolean mShowAirplaneMode;
+ private final AtomicBoolean mNetworkSupported = new AtomicBoolean();
@VisibleForTesting
protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private WifiManager mWifiManager;
@@ -78,12 +79,14 @@
new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedWakingUp() {
- if (mCarrierTextCallback != null) mCarrierTextCallback.finishedWakingUp();
+ final CarrierTextCallback callback = mCarrierTextCallback;
+ if (callback != null) callback.finishedWakingUp();
}
@Override
public void onStartedGoingToSleep() {
- if (mCarrierTextCallback != null) mCarrierTextCallback.startedGoingToSleep();
+ final CarrierTextCallback callback = mCarrierTextCallback;
+ if (callback != null) callback.startedGoingToSleep();
}
};
@@ -131,7 +134,7 @@
@Override
public void onActiveDataSubscriptionIdChanged(int subId) {
mActiveMobileDataSubscription = subId;
- if (mKeyguardUpdateMonitor != null) {
+ if (mNetworkSupported.get() && mCarrierTextCallback != null) {
updateCarrierText();
}
}
@@ -173,6 +176,17 @@
mSimSlotsNumber = getTelephonyManager().getSupportedModemCount();
mSimErrorState = new boolean[mSimSlotsNumber];
mMainHandler = Dependency.get(Dependency.MAIN_HANDLER);
+ mBgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
+ mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
+ mBgHandler.post(() -> {
+ boolean supported = ConnectivityManager.from(mContext).isNetworkSupported(
+ ConnectivityManager.TYPE_MOBILE);
+ if (supported && mNetworkSupported.compareAndSet(false, supported)) {
+ // This will set/remove the listeners appropriately. Note that it will never double
+ // add the listeners.
+ handleSetListening(mCarrierTextCallback);
+ }
+ });
}
private TelephonyManager getTelephonyManager() {
@@ -221,48 +235,51 @@
}
/**
- * Sets the listening status of this controller. If the callback is null, it is set to
- * not listening
+ * This may be called internally after retrieving the correct value of {@code mNetworkSupported}
+ * (assumed false to start). In that case, the following happens:
+ * <ul>
+ * <li> If there was a registered callback, and the network is supported, it will register
+ * listeners.
+ * <li> If there was not a registered callback, it will try to remove unregistered listeners
+ * which is a no-op
+ * </ul>
*
- * @param callback Callback to provide text updates
+ * This call will always be processed in a background thread.
*/
- public void setListening(CarrierTextCallback callback) {
+ private void handleSetListening(CarrierTextCallback callback) {
TelephonyManager telephonyManager = getTelephonyManager();
if (callback != null) {
mCarrierTextCallback = callback;
- // TODO(b/140034799)
- if (whitelistIpcs(() -> ConnectivityManager.from(mContext).isNetworkSupported(
- ConnectivityManager.TYPE_MOBILE))) {
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
+ if (mNetworkSupported.get()) {
// Keyguard update monitor expects callbacks from main thread
- mMainHandler.post(() -> {
- if (mKeyguardUpdateMonitor != null) {
- mKeyguardUpdateMonitor.registerCallback(mCallback);
- }
- });
+ mMainHandler.post(() -> mKeyguardUpdateMonitor.registerCallback(mCallback));
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
telephonyManager.listen(mPhoneStateListener,
LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
} else {
// Don't listen and clear out the text when the device isn't a phone.
- mKeyguardUpdateMonitor = null;
- callback.updateCarrierInfo(new CarrierTextCallbackInfo("", null, false, null));
+ mMainHandler.post(() -> callback.updateCarrierInfo(
+ new CarrierTextCallbackInfo("", null, false, null)
+ ));
}
} else {
mCarrierTextCallback = null;
- if (mKeyguardUpdateMonitor != null) {
- // Keyguard update monitor expects callbacks from main thread
- mMainHandler.post(() -> {
- if (mKeyguardUpdateMonitor != null) {
- mKeyguardUpdateMonitor.removeCallback(mCallback);
- }
- });
- mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
- }
+ mMainHandler.post(() -> mKeyguardUpdateMonitor.removeCallback(mCallback));
+ mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
telephonyManager.listen(mPhoneStateListener, LISTEN_NONE);
}
}
+ /**
+ * Sets the listening status of this controller. If the callback is null, it is set to
+ * not listening.
+ *
+ * @param callback Callback to provide text updates
+ */
+ public void setListening(CarrierTextCallback callback) {
+ mBgHandler.post(() -> handleSetListening(callback));
+ }
+
protected List<SubscriptionInfo> getSubscriptionInfo() {
return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
}
@@ -500,7 +517,7 @@
*/
private CarrierTextController.StatusMode getStatusForIccState(int simState) {
final boolean missingAndNotProvisioned =
- !Dependency.get(KeyguardUpdateMonitor.class).isDeviceProvisioned()
+ !mKeyguardUpdateMonitor.isDeviceProvisioned()
&& (simState == TelephonyManager.SIM_STATE_ABSENT
|| simState == TelephonyManager.SIM_STATE_PERM_DISABLED);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 9d1dfa7..1dd6409 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -34,7 +34,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
@@ -635,7 +634,6 @@
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("BiometricPrompt");
lp.token = windowToken;
- lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
return lp;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index f1b401e..f044389 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -16,6 +16,7 @@
package com.android.systemui.bubbles;
+import static android.app.Notification.FLAG_BUBBLE;
import static android.os.AsyncTask.Status.FINISHED;
import static android.view.Display.INVALID_DISPLAY;
@@ -27,6 +28,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
@@ -55,6 +57,11 @@
class Bubble implements BubbleViewProvider {
private static final String TAG = "Bubble";
+ /**
+ * NotificationEntry associated with the bubble. A null value implies this bubble is loaded
+ * from disk.
+ */
+ @Nullable
private NotificationEntry mEntry;
private final String mKey;
private final String mGroupId;
@@ -95,18 +102,56 @@
private Bitmap mBadgedImage;
private int mDotColor;
private Path mDotPath;
+ private int mFlags;
-
+ /**
+ * Extract GroupId from {@link NotificationEntry}. See also {@link #groupId(ShortcutInfo)}.
+ */
public static String groupId(NotificationEntry entry) {
UserHandle user = entry.getSbn().getUser();
return user.getIdentifier() + "|" + entry.getSbn().getPackageName();
}
- // TODO: Decouple Bubble from NotificationEntry and transform ShortcutInfo into Bubble
+ /**
+ * Extract GroupId from {@link ShortcutInfo}. This should match the one generated from
+ * {@link NotificationEntry}. See also {@link #groupId(NotificationEntry)}.
+ */
+ @NonNull
+ public static String groupId(@NonNull final ShortcutInfo shortcutInfo) {
+ return shortcutInfo.getUserId() + "|" + shortcutInfo.getPackage();
+ }
+
+ /**
+ * Generate a unique identifier for this bubble based on given {@link NotificationEntry}. If
+ * {@link ShortcutInfo} was found in the notification entry, the identifier would be generated
+ * from {@link ShortcutInfo} instead. See also {@link #key(ShortcutInfo)}.
+ */
+ @NonNull
+ public static String key(@NonNull final NotificationEntry entry) {
+ final ShortcutInfo shortcutInfo = entry.getRanking().getShortcutInfo();
+ if (shortcutInfo != null) return key(shortcutInfo);
+ return entry.getKey();
+ }
+
+ /**
+ * Generate a unique identifier for this bubble based on given {@link ShortcutInfo}.
+ * See also {@link #key(NotificationEntry)}.
+ */
+ @NonNull
+ public static String key(@NonNull final ShortcutInfo shortcutInfo) {
+ return shortcutInfo.getUserId() + "|" + shortcutInfo.getPackage() + "|"
+ + shortcutInfo.getId();
+ }
+
+ /**
+ * Create a bubble with limited information based on given {@link ShortcutInfo}.
+ * Note: Currently this is only being used when the bubble is persisted to disk.
+ */
Bubble(ShortcutInfo shortcutInfo) {
mShortcutInfo = shortcutInfo;
- mKey = shortcutInfo.getId();
- mGroupId = shortcutInfo.getId();
+ mKey = key(shortcutInfo);
+ mGroupId = groupId(shortcutInfo);
+ mFlags = 0;
}
/** Used in tests when no UI is required. */
@@ -114,10 +159,11 @@
Bubble(NotificationEntry e,
BubbleController.NotificationSuppressionChangedListener listener) {
mEntry = e;
- mKey = e.getKey();
+ mKey = key(e);
mLastUpdated = e.getSbn().getPostTime();
mGroupId = groupId(e);
mSuppressionListener = listener;
+ mFlags = e.getSbn().getNotification().flags;
}
@Override
@@ -125,16 +171,26 @@
return mKey;
}
+ @Nullable
public NotificationEntry getEntry() {
return mEntry;
}
+ @Nullable
+ public UserHandle getUser() {
+ if (mEntry != null) return mEntry.getSbn().getUser();
+ if (mShortcutInfo != null) return mShortcutInfo.getUserHandle();
+ return null;
+ }
+
public String getGroupId() {
return mGroupId;
}
public String getPackageName() {
- return mEntry.getSbn().getPackageName();
+ return mEntry == null
+ ? mShortcutInfo == null ? null : mShortcutInfo.getPackage()
+ : mEntry.getSbn().getPackageName();
}
@Override
@@ -218,7 +274,8 @@
void inflate(BubbleViewInfoTask.Callback callback,
Context context,
BubbleStackView stackView,
- BubbleIconFactory iconFactory) {
+ BubbleIconFactory iconFactory,
+ boolean skipInflation) {
if (isBubbleLoading()) {
mInflationTask.cancel(true /* mayInterruptIfRunning */);
}
@@ -226,6 +283,7 @@
context,
stackView,
iconFactory,
+ skipInflation,
callback);
if (mInflateSynchronously) {
mInflationTask.onPostExecute(mInflationTask.doInBackground());
@@ -345,6 +403,7 @@
* Whether this notification should be shown in the shade.
*/
boolean showInShade() {
+ if (mEntry == null) return false;
return !shouldSuppressNotification() || !mEntry.isClearable();
}
@@ -352,8 +411,8 @@
* Sets whether this notification should be suppressed in the shade.
*/
void setSuppressNotification(boolean suppressNotification) {
+ if (mEntry == null) return;
boolean prevShowInShade = showInShade();
-
Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
int flags = data.getFlags();
if (suppressNotification) {
@@ -384,6 +443,7 @@
*/
@Override
public boolean showDot() {
+ if (mEntry == null) return false;
return mShowBubbleUpdateDot
&& !mEntry.shouldSuppressNotificationDot()
&& !shouldSuppressNotification();
@@ -393,6 +453,7 @@
* Whether the flyout for the bubble should be shown.
*/
boolean showFlyout() {
+ if (mEntry == null) return false;
return !mSuppressFlyout && !mEntry.shouldSuppressPeek()
&& !shouldSuppressNotification()
&& !mEntry.shouldSuppressNotificationList();
@@ -416,11 +477,13 @@
* is an ongoing bubble.
*/
boolean isOngoing() {
+ if (mEntry == null) return false;
int flags = mEntry.getSbn().getNotification().flags;
return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
}
float getDesiredHeight(Context context) {
+ if (mEntry == null) return 0;
Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
boolean useRes = data.getDesiredHeightResId() != 0;
if (useRes) {
@@ -434,6 +497,7 @@
}
String getDesiredHeightString() {
+ if (mEntry == null) return String.valueOf(0);
Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
boolean useRes = data.getDesiredHeightResId() != 0;
if (useRes) {
@@ -450,11 +514,13 @@
* To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}.
*/
boolean usingShortcutInfo() {
- return mEntry.getBubbleMetadata().getShortcutId() != null;
+ return mEntry != null && mEntry.getBubbleMetadata().getShortcutId() != null
+ || mShortcutInfo != null;
}
@Nullable
PendingIntent getBubbleIntent() {
+ if (mEntry == null) return null;
Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
if (data != null) {
return data.getIntent();
@@ -462,16 +528,32 @@
return null;
}
- Intent getSettingsIntent() {
+ Intent getSettingsIntent(final Context context) {
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
- intent.putExtra(Settings.EXTRA_APP_UID, mEntry.getSbn().getUid());
+ final int uid = getUid(context);
+ if (uid != -1) {
+ intent.putExtra(Settings.EXTRA_APP_UID, uid);
+ }
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
return intent;
}
+ private int getUid(final Context context) {
+ if (mEntry != null) return mEntry.getSbn().getUid();
+ final PackageManager pm = context.getPackageManager();
+ if (pm == null) return -1;
+ try {
+ final ApplicationInfo info = pm.getApplicationInfo(mShortcutInfo.getPackage(), 0);
+ return info.uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "cannot find uid", e);
+ }
+ return -1;
+ }
+
private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) {
PackageManager pm = context.getPackageManager();
Resources r;
@@ -493,15 +575,30 @@
}
private boolean shouldSuppressNotification() {
+ if (mEntry == null) return false;
return mEntry.getBubbleMetadata() != null
&& mEntry.getBubbleMetadata().isNotificationSuppressed();
}
boolean shouldAutoExpand() {
+ if (mEntry == null) return false;
Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata();
return metadata != null && metadata.getAutoExpandBubble();
}
+ public boolean isBubble() {
+ if (mEntry == null) return (mFlags & FLAG_BUBBLE) != 0;
+ return (mEntry.getSbn().getNotification().flags & FLAG_BUBBLE) != 0;
+ }
+
+ public void enable(int option) {
+ mFlags |= option;
+ }
+
+ public void disable(int option) {
+ mFlags &= ~option;
+ }
+
@Override
public String toString() {
return "Bubble{" + mKey + '}';
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 6a5e94c..8707d38 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -42,6 +42,7 @@
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.INotificationManager;
@@ -198,6 +199,11 @@
/** Last known orientation, used to detect orientation changes in {@link #onConfigChanged}. */
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+ /**
+ * Last known screen density, used to detect display size changes in {@link #onConfigChanged}.
+ */
+ private int mDensityDpi = Configuration.DENSITY_DPI_UNDEFINED;
+
private boolean mInflateSynchronously;
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
@@ -237,7 +243,7 @@
* This can happen when an app cancels a bubbled notification or when the user dismisses a
* bubble.
*/
- void removeNotification(NotificationEntry entry, int reason);
+ void removeNotification(@NonNull NotificationEntry entry, int reason);
/**
* Called when a bubbled notification has changed whether it should be
@@ -253,7 +259,7 @@
* removes all remnants of the group's summary from the notification pipeline.
* TODO: (b/145659174) Only old pipeline needs this - delete post-migration.
*/
- void maybeCancelSummary(NotificationEntry entry);
+ void maybeCancelSummary(@NonNull NotificationEntry entry);
}
/**
@@ -476,7 +482,7 @@
addNotifCallback(new NotifCallback() {
@Override
- public void removeNotification(NotificationEntry entry, int reason) {
+ public void removeNotification(@NonNull final NotificationEntry entry, int reason) {
mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
reason);
}
@@ -487,7 +493,7 @@
}
@Override
- public void maybeCancelSummary(NotificationEntry entry) {
+ public void maybeCancelSummary(@NonNull final NotificationEntry entry) {
// Check if removed bubble has an associated suppressed group summary that needs
// to be removed now.
final String groupKey = entry.getSbn().getGroupKey();
@@ -696,18 +702,27 @@
mBubbleIconFactory = new BubbleIconFactory(mContext);
// Reload each bubble
for (Bubble b: mBubbleData.getBubbles()) {
- b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory);
+ b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory,
+ false /* skipInflation */);
}
for (Bubble b: mBubbleData.getOverflowBubbles()) {
- b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory);
+ b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory,
+ false /* skipInflation */);
}
}
@Override
public void onConfigChanged(Configuration newConfig) {
- if (mStackView != null && newConfig != null && newConfig.orientation != mOrientation) {
- mOrientation = newConfig.orientation;
- mStackView.onOrientationChanged(newConfig.orientation);
+ if (mStackView != null && newConfig != null) {
+ if (newConfig.orientation != mOrientation) {
+ mOrientation = newConfig.orientation;
+ mStackView.onOrientationChanged(newConfig.orientation);
+ }
+ if (newConfig.densityDpi != mDensityDpi) {
+ mDensityDpi = newConfig.densityDpi;
+ mBubbleIconFactory = new BubbleIconFactory(mContext);
+ mStackView.onDisplaySizeChanged();
+ }
}
}
@@ -791,7 +806,7 @@
if (bubble != null) {
mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
}
- } else if (bubble.getEntry().isBubble()){
+ } else if (bubble.isBubble()) {
mBubbleData.setSelectedBubble(bubble);
}
mBubbleData.setExpanded(true);
@@ -820,10 +835,33 @@
updateBubble(notif, suppressFlyout, true /* showInShade */);
}
+ /**
+ * Fills the overflow bubbles by loading them from disk.
+ */
+ void loadOverflowBubblesFromDisk() {
+ if (!mBubbleData.getOverflowBubbles().isEmpty()) {
+ // we don't need to load overflow bubbles from disk if it is already in memory
+ return;
+ }
+ mDataRepository.loadBubbles((bubbles) -> {
+ bubbles.forEach(bubble -> {
+ if (mBubbleData.getBubbles().contains(bubble)) {
+ // if the bubble is already active, there's no need to push it to overflow
+ return;
+ }
+ bubble.inflate((b) -> mBubbleData.overflowBubble(DISMISS_AGED, bubble),
+ mContext, mStackView, mBubbleIconFactory, true /* skipInflation */);
+ });
+ return null;
+ });
+ }
+
void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) {
if (mStackView == null) {
// Lazy init stack view when a bubble is created
ensureStackViewCreated();
+ // Lazy load overflow bubbles from disk
+ loadOverflowBubblesFromDisk();
}
// If this is an interruptive notif, mark that it's interrupted
if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
@@ -843,11 +881,11 @@
return;
}
mHandler.post(
- () -> removeBubble(bubble.getEntry(),
+ () -> removeBubble(bubble.getKey(),
BubbleController.DISMISS_INVALID_INTENT));
});
},
- mContext, mStackView, mBubbleIconFactory);
+ mContext, mStackView, mBubbleIconFactory, false /* skipInflation */);
}
/**
@@ -859,7 +897,10 @@
* @param entry the notification to change bubble state for.
* @param shouldBubble whether the notification should show as a bubble or not.
*/
- public void onUserChangedBubble(NotificationEntry entry, boolean shouldBubble) {
+ public void onUserChangedBubble(@Nullable final NotificationEntry entry, boolean shouldBubble) {
+ if (entry == null) {
+ return;
+ }
NotificationChannel channel = entry.getChannel();
final String appPkg = entry.getSbn().getPackageName();
final int appUid = entry.getSbn().getUid();
@@ -870,6 +911,7 @@
// Update the state in NotificationManagerService
try {
int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE;
mBarService.onNotificationBubbleChanged(entry.getKey(), shouldBubble, flags);
} catch (RemoteException e) {
}
@@ -897,14 +939,14 @@
}
/**
- * Removes the bubble with the given NotificationEntry.
+ * Removes the bubble with the given key.
* <p>
* Must be called from the main thread.
*/
@MainThread
- void removeBubble(NotificationEntry entry, int reason) {
- if (mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
- mBubbleData.notificationEntryRemoved(entry, reason);
+ void removeBubble(String key, int reason) {
+ if (mBubbleData.hasAnyBubbleWithKey(key)) {
+ mBubbleData.notificationEntryRemoved(key, reason);
}
}
@@ -920,7 +962,7 @@
&& canLaunchInActivityView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
- removeBubble(entry, DISMISS_NO_LONGER_BUBBLE);
+ removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
} else if (shouldBubble) {
updateBubble(entry);
}
@@ -934,10 +976,10 @@
// Remove any associated bubble children with the summary
final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
for (int i = 0; i < bubbleChildren.size(); i++) {
- removeBubble(bubbleChildren.get(i).getEntry(), DISMISS_GROUP_CANCELLED);
+ removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED);
}
} else {
- removeBubble(entry, DISMISS_NOTIF_CANCEL);
+ removeBubble(entry.getKey(), DISMISS_NOTIF_CANCEL);
}
}
@@ -959,7 +1001,8 @@
rankingMap.getRanking(key, mTmpRanking);
boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
if (isActiveBubble && !mTmpRanking.canBubble()) {
- mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
+ mBubbleData.notificationEntryRemoved(entry.getKey(),
+ BubbleController.DISMISS_BLOCKED);
} else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
entry.setFlagBubble(true);
onEntryUpdated(entry);
@@ -969,9 +1012,15 @@
private void setIsBubble(Bubble b, boolean isBubble) {
if (isBubble) {
- b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE;
+ if (b.getEntry() != null) {
+ b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE;
+ }
+ b.enable(FLAG_BUBBLE);
} else {
- b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
+ if (b.getEntry() != null) {
+ b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
+ }
+ b.disable(FLAG_BUBBLE);
}
try {
mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0);
@@ -1019,23 +1068,27 @@
// The bubble is now gone & the notification is hidden from the shade, so
// time to actually remove it
for (NotifCallback cb : mCallbacks) {
- cb.removeNotification(bubble.getEntry(), REASON_CANCEL);
+ if (bubble.getEntry() != null) {
+ cb.removeNotification(bubble.getEntry(), REASON_CANCEL);
+ }
}
} else {
- if (bubble.getEntry().isBubble() && bubble.showInShade()) {
+ if (bubble.isBubble() && bubble.showInShade()) {
setIsBubble(bubble, /* isBubble */ false);
}
- if (bubble.getEntry().getRow() != null) {
+ if (bubble.getEntry() != null && bubble.getEntry().getRow() != null) {
bubble.getEntry().getRow().updateBubbleButton();
}
}
}
- final String groupKey = bubble.getEntry().getSbn().getGroupKey();
- if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
- // Time to potentially remove the summary
- for (NotifCallback cb : mCallbacks) {
- cb.maybeCancelSummary(bubble.getEntry());
+ if (bubble.getEntry() != null) {
+ final String groupKey = bubble.getEntry().getSbn().getGroupKey();
+ if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
+ // Time to potentially remove the summary
+ for (NotifCallback cb : mCallbacks) {
+ cb.maybeCancelSummary(bubble.getEntry());
+ }
}
}
}
@@ -1060,7 +1113,7 @@
if (update.selectionChanged) {
mStackView.setSelectedBubble(update.selectedBubble);
- if (update.selectedBubble != null) {
+ if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) {
mNotificationGroupManager.updateSuppression(
update.selectedBubble.getEntry());
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 65d5beb..7b323ce 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -23,6 +23,7 @@
import static java.util.stream.Collectors.toList;
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@@ -223,7 +224,7 @@
false, /* showInShade */ true);
setSelectedBubble(bubble);
},
- mContext, stack, factory);
+ mContext, stack, factory, false /* skipInflation */);
dispatchPendingChanges();
}
@@ -278,7 +279,8 @@
}
mPendingBubbles.remove(bubble); // No longer pending once we're here
Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey());
- suppressFlyout |= !bubble.getEntry().getRanking().visuallyInterruptive();
+ suppressFlyout |= bubble.getEntry() == null
+ || !bubble.getEntry().getRanking().visuallyInterruptive();
if (prevBubble == null) {
// Create a new bubble
@@ -307,11 +309,14 @@
dispatchPendingChanges();
}
- public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
+ /**
+ * Called when a notification associated with a bubble is removed.
+ */
+ public void notificationEntryRemoved(String key, @DismissReason int reason) {
if (DEBUG_BUBBLE_DATA) {
- Log.d(TAG, "notificationEntryRemoved: entry=" + entry + " reason=" + reason);
+ Log.d(TAG, "notificationEntryRemoved: key=" + key + " reason=" + reason);
}
- doRemove(entry.getKey(), reason);
+ doRemove(key, reason);
dispatchPendingChanges();
}
@@ -359,7 +364,7 @@
return bubbleChildren;
}
for (Bubble b : mBubbles) {
- if (groupKey.equals(b.getEntry().getSbn().getGroupKey())) {
+ if (b.getEntry() != null && groupKey.equals(b.getEntry().getSbn().getGroupKey())) {
bubbleChildren.add(b);
}
}
@@ -470,7 +475,9 @@
Bubble newSelected = mBubbles.get(newIndex);
setSelectedBubbleInternal(newSelected);
}
- maybeSendDeleteIntent(reason, bubbleToRemove.getEntry());
+ if (bubbleToRemove.getEntry() != null) {
+ maybeSendDeleteIntent(reason, bubbleToRemove.getEntry());
+ }
}
void overflowBubble(@DismissReason int reason, Bubble bubble) {
@@ -744,7 +751,8 @@
return true;
}
- private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) {
+ private void maybeSendDeleteIntent(@DismissReason int reason,
+ @NonNull final NotificationEntry entry) {
if (reason == BubbleController.DISMISS_USER_GESTURE) {
Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata();
PendingIntent deleteIntent = bubbleMetadata != null
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
index ba93f41..6df3132 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
@@ -74,7 +74,10 @@
private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> {
return bubbles.mapNotNull { b ->
- val shortcutId = b.shortcutInfo?.id ?: return@mapNotNull null
+ var shortcutId = b.shortcutInfo?.id
+ if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId
+ if (shortcutId == null) shortcutId = b.entry?.ranking?.shortcutInfo?.id
+ if (shortcutId == null) return@mapNotNull null
BubbleEntity(userId, b.packageName, shortcutId)
}
}
@@ -108,7 +111,6 @@
/**
* Load bubbles from disk.
*/
- // TODO: call this method from BubbleController and update UI
@SuppressLint("WrongConstant")
fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index baf92dc..0a1a0f7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -65,7 +65,6 @@
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
import com.android.systemui.statusbar.AlphaOptimizedButton;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* Container for the expanded bubble view, handles rendering the caret and settings icon.
@@ -161,7 +160,7 @@
// the bubble again so we'll just remove it.
Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+ ", " + e.getMessage() + "; removing bubble");
- mBubbleController.removeBubble(getBubbleEntry(),
+ mBubbleController.removeBubble(getBubbleKey(),
BubbleController.DISMISS_INVALID_INTENT);
}
});
@@ -205,7 +204,7 @@
}
if (mBubble != null) {
// Must post because this is called from a binder thread.
- post(() -> mBubbleController.removeBubble(mBubble.getEntry(),
+ post(() -> mBubbleController.removeBubble(mBubble.getKey(),
BubbleController.DISMISS_TASK_FINISHED));
}
}
@@ -292,10 +291,6 @@
return mBubble != null ? mBubble.getKey() : "null";
}
- private NotificationEntry getBubbleEntry() {
- return mBubble != null ? mBubble.getEntry() : null;
- }
-
void setManageClickListener(OnClickListener manageClickListener) {
findViewById(R.id.settings_button).setOnClickListener(manageClickListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
index e96bef3..f4eb580 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -58,12 +58,13 @@
public BubbleOverflow(Context context) {
mContext = context;
mInflater = LayoutInflater.from(context);
- mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
- mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.bubble_overflow_icon_bitmap_size);
}
void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
+ mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
+ mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.bubble_overflow_icon_bitmap_size);
+
mExpandedView = (BubbleExpandedView) mInflater.inflate(
R.layout.bubble_expanded_view, parentViewGroup /* root */,
false /* attachToRoot */);
@@ -74,6 +75,7 @@
}
void updateIcon(Context context, ViewGroup parentViewGroup) {
+ mContext = context;
mInflater = LayoutInflater.from(context);
mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button,
parentViewGroup /* root */,
@@ -87,7 +89,7 @@
ta.recycle();
TypedValue typedValue = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
+ mContext.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
int colorAccent = mContext.getColor(typedValue.resourceId);
mOverflowBtn.getDrawable().setTint(colorAccent);
mDotColor = colorAccent;
@@ -97,7 +99,7 @@
mBitmapSize - mIconBitmapSize /* inset */);
AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(bg, fg);
- BubbleIconFactory iconFactory = new BubbleIconFactory(context);
+ BubbleIconFactory iconFactory = new BubbleIconFactory(mContext);
mIcon = iconFactory.createBadgedIconBitmap(adaptiveIconDrawable,
null /* user */,
true /* shrinkNonAdaptiveIcons */).icon;
@@ -106,7 +108,7 @@
null /* outBounds */, null /* path */, null /* outMaskShape */);
float radius = DEFAULT_PATH_SIZE / 2f;
mPath = PathParser.createPathFromPathData(
- context.getResources().getString(com.android.internal.R.string.config_icon_mask));
+ mContext.getResources().getString(com.android.internal.R.string.config_icon_mask));
Matrix matrix = new Matrix();
matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
radius /* pivot y */);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 0074249..08ec789 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -41,6 +41,7 @@
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.R;
import java.util.ArrayList;
@@ -214,6 +215,8 @@
@Override
public BubbleOverflowAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
+
+ // Set layout for overflow bubble view.
LinearLayout overflowView = (LinearLayout) LayoutInflater.from(parent.getContext())
.inflate(R.layout.bubble_overflow_view, parent, false);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
@@ -222,6 +225,18 @@
params.width = mWidth;
params.height = mHeight;
overflowView.setLayoutParams(params);
+
+ // Ensure name has enough contrast.
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[]{android.R.attr.colorBackgroundFloating, android.R.attr.textColorPrimary});
+ final int bgColor = ta.getColor(0, Color.WHITE);
+ int textColor = ta.getColor(1, Color.BLACK);
+ textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
+ ta.recycle();
+
+ TextView viewName = overflowView.findViewById(R.id.bubble_view_name);
+ viewName.setTextColor(textColor);
+
return new ViewHolder(overflowView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index cb6e736..e35b203 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -49,7 +49,6 @@
import android.graphics.Region;
import android.os.Bundle;
import android.os.Vibrator;
-import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.Choreographer;
import android.view.DisplayCutout;
@@ -445,6 +444,10 @@
final boolean clickedBubbleIsCurrentlyExpandedBubble =
clickedBubble.getKey().equals(mExpandedBubble.getKey());
+ if (isExpanded()) {
+ mExpandedAnimationController.onGestureFinished();
+ }
+
if (isExpanded() && !clickedBubbleIsCurrentlyExpandedBubble) {
if (clickedBubble != mBubbleData.getSelectedBubble()) {
// Select the clicked bubble.
@@ -464,7 +467,6 @@
mBubbleData.setExpanded(!mBubbleData.isExpanded());
}
}
- mExpandedAnimationController.onGestureFinished();
}
};
@@ -787,8 +789,8 @@
mOrientationChangedListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- mExpandedAnimationController.updateOrientation(mOrientation, mDisplaySize);
- mStackAnimationController.updateOrientation(mOrientation);
+ mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
+ mStackAnimationController.updateResources(mOrientation);
// Reposition & adjust the height for new orientation
if (mIsExpanded) {
@@ -916,10 +918,10 @@
showManageMenu(false /* show */);
final Bubble bubble = mBubbleData.getSelectedBubble();
if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
- final Intent intent = bubble.getSettingsIntent();
+ final Intent intent = bubble.getSettingsIntent(mContext);
collapseStack(() -> {
- mContext.startActivityAsUser(
- intent, bubble.getEntry().getSbn().getUser());
+
+ mContext.startActivityAsUser(intent, bubble.getUser());
logBubbleClickEvent(
bubble,
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
@@ -1004,7 +1006,7 @@
mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
} else {
mBubbleContainer.removeView(mBubbleOverflow.getBtn());
- mBubbleOverflow.updateIcon(mContext, this);
+ mBubbleOverflow.updateIcon(mContext,this);
overflowBtnIndex = mBubbleContainer.getChildCount();
}
mBubbleContainer.addView(mBubbleOverflow.getBtn(), overflowBtnIndex,
@@ -1051,6 +1053,28 @@
mShowingManage = false;
}
+ /** Respond to the display size change by recalculating view size and location. */
+ public void onDisplaySizeChanged() {
+ setUpOverflow();
+
+ WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getRealSize(mDisplaySize);
+ Resources res = getContext().getResources();
+ mStatusBarHeight = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+ mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
+ mBubbleSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
+ for (Bubble b : mBubbleData.getBubbles()) {
+ if (b.getIconView() == null) {
+ Log.d(TAG, "Display size changed. Icon null: " + b);
+ continue;
+ }
+ b.getIconView().setLayoutParams(new LayoutParams(mBubbleSize, mBubbleSize));
+ }
+ mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
+ mStackAnimationController.updateResources(mOrientation);
+ }
+
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
@@ -1154,13 +1178,19 @@
for (int i = 0; i < mBubbleData.getBubbles().size(); i++) {
final Bubble bubble = mBubbleData.getBubbles().get(i);
final String appName = bubble.getAppName();
- final Notification notification = bubble.getEntry().getSbn().getNotification();
- final CharSequence titleCharSeq =
- notification.extras.getCharSequence(Notification.EXTRA_TITLE);
- String titleStr = getResources().getString(R.string.notification_bubble_title);
+ final CharSequence titleCharSeq;
+ if (bubble.getEntry() == null) {
+ titleCharSeq = null;
+ } else {
+ titleCharSeq = bubble.getEntry().getSbn().getNotification().extras.getCharSequence(
+ Notification.EXTRA_TITLE);
+ }
+ final String titleStr;
if (titleCharSeq != null) {
titleStr = titleCharSeq.toString();
+ } else {
+ titleStr = getResources().getString(R.string.notification_bubble_title);
}
if (bubble.getIconView() != null) {
@@ -1291,7 +1321,7 @@
Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
}
- private void updateOverflowBtnVisibility(boolean apply) {
+ private void updateOverflowBtnVisibility() {
if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
return;
}
@@ -1300,11 +1330,6 @@
Log.d(TAG, "Show overflow button.");
}
mBubbleOverflow.setBtnVisible(VISIBLE);
- if (apply) {
- mExpandedAnimationController.expandFromStack(() -> {
- updatePointerPosition();
- } /* after */);
- }
} else {
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "Collapsed. Hide overflow button.");
@@ -1564,7 +1589,7 @@
Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(),
mExpandedBubble));
}
- updateOverflowBtnVisibility(/* apply */ false);
+ updateOverflowBtnVisibility();
mBubbleContainer.cancelAllAnimations();
mExpandedAnimationController.collapseBackToStack(
mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
@@ -1588,7 +1613,7 @@
beforeExpandedViewAnimation();
mBubbleContainer.setActiveController(mExpandedAnimationController);
- updateOverflowBtnVisibility(/* apply */ false);
+ updateOverflowBtnVisibility();
mExpandedAnimationController.expandFromStack(() -> {
updatePointerPosition();
afterExpandedViewAnimation();
@@ -1778,7 +1803,7 @@
private void dismissBubbleIfExists(@Nullable Bubble bubble) {
if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
mBubbleData.notificationEntryRemoved(
- bubble.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ bubble.getKey(), BubbleController.DISMISS_USER_GESTURE);
}
}
@@ -2276,18 +2301,12 @@
* @param action the user interaction enum.
*/
private void logBubbleClickEvent(Bubble bubble, int action) {
- StatusBarNotification notification = bubble.getEntry().getSbn();
- SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
- notification.getPackageName(),
- notification.getNotification().getChannelId(),
- notification.getId(),
- getBubbleIndex(getExpandedBubble()),
+ bubble.logUIEvent(
getBubbleCount(),
action,
getNormalizedXPosition(),
getNormalizedYPosition(),
- bubble.showInShade(),
- bubble.isOngoing(),
- false /* isAppForeground (unused) */);
+ getBubbleIndex(getExpandedBubble())
+ );
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 8a57a73..525d5b5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -37,6 +37,7 @@
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
@@ -74,6 +75,7 @@
private WeakReference<Context> mContext;
private WeakReference<BubbleStackView> mStackView;
private BubbleIconFactory mIconFactory;
+ private boolean mSkipInflation;
private Callback mCallback;
/**
@@ -84,17 +86,20 @@
Context context,
BubbleStackView stackView,
BubbleIconFactory factory,
+ boolean skipInflation,
Callback c) {
mBubble = b;
mContext = new WeakReference<>(context);
mStackView = new WeakReference<>(stackView);
mIconFactory = factory;
+ mSkipInflation = skipInflation;
mCallback = c;
}
@Override
protected BubbleViewInfo doInBackground(Void... voids) {
- return BubbleViewInfo.populate(mContext.get(), mStackView.get(), mIconFactory, mBubble);
+ return BubbleViewInfo.populate(mContext.get(), mStackView.get(), mIconFactory, mBubble,
+ mSkipInflation);
}
@Override
@@ -123,11 +128,36 @@
@Nullable
static BubbleViewInfo populate(Context c, BubbleStackView stackView,
- BubbleIconFactory iconFactory, Bubble b) {
+ BubbleIconFactory iconFactory, Bubble b, boolean skipInflation) {
+ final NotificationEntry entry = b.getEntry();
+ if (entry == null) {
+ // populate from ShortcutInfo when NotificationEntry is not available
+ final ShortcutInfo s = b.getShortcutInfo();
+ return populate(c, stackView, iconFactory, skipInflation || b.isInflated(),
+ s.getPackage(), s.getUserHandle(), s, null);
+ }
+ final StatusBarNotification sbn = entry.getSbn();
+ final String bubbleShortcutId = entry.getBubbleMetadata().getShortcutId();
+ final ShortcutInfo si = bubbleShortcutId == null
+ ? null : entry.getRanking().getShortcutInfo();
+ return populate(
+ c, stackView, iconFactory, skipInflation || b.isInflated(),
+ sbn.getPackageName(), sbn.getUser(), si, entry);
+ }
+
+ private static BubbleViewInfo populate(
+ @NonNull final Context c,
+ @NonNull final BubbleStackView stackView,
+ @NonNull final BubbleIconFactory iconFactory,
+ final boolean isInflated,
+ @NonNull final String packageName,
+ @NonNull final UserHandle user,
+ @Nullable final ShortcutInfo shortcutInfo,
+ @Nullable final NotificationEntry entry) {
BubbleViewInfo info = new BubbleViewInfo();
// View inflation: only should do this once per bubble
- if (!b.isInflated()) {
+ if (!isInflated) {
LayoutInflater inflater = LayoutInflater.from(c);
info.imageView = (BadgedImageView) inflater.inflate(
R.layout.bubble_view, stackView, false /* attachToRoot */);
@@ -137,12 +167,8 @@
info.expandedView.setStackView(stackView);
}
- StatusBarNotification sbn = b.getEntry().getSbn();
- String packageName = sbn.getPackageName();
-
- String bubbleShortcutId = b.getEntry().getBubbleMetadata().getShortcutId();
- if (bubbleShortcutId != null) {
- info.shortcutInfo = b.getEntry().getRanking().getShortcutInfo();
+ if (shortcutInfo != null) {
+ info.shortcutInfo = shortcutInfo;
}
// App name & app icon
@@ -161,7 +187,7 @@
info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
}
appIcon = pm.getApplicationIcon(packageName);
- badgedIcon = pm.getUserBadgedIcon(appIcon, sbn.getUser());
+ badgedIcon = pm.getUserBadgedIcon(appIcon, user);
} catch (PackageManager.NameNotFoundException exception) {
// If we can't find package... don't think we should show the bubble.
Log.w(TAG, "Unable to find package: " + packageName);
@@ -170,7 +196,7 @@
// Badged bubble image
Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
- b.getEntry().getBubbleMetadata());
+ entry == null ? null : entry.getBubbleMetadata());
if (bubbleDrawable == null) {
// Default to app icon
bubbleDrawable = appIcon;
@@ -196,7 +222,9 @@
Color.WHITE, WHITE_SCRIM_ALPHA);
// Flyout
- info.flyoutMessage = extractFlyoutMessage(c, b.getEntry());
+ if (entry != null) {
+ info.flyoutMessage = extractFlyoutMessage(c, entry);
+ }
return info;
}
}
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 35406c7..f57cf42 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -118,7 +118,7 @@
public ExpandedAnimationController(Point displaySize, int expandedViewPadding,
int orientation) {
- updateOrientation(orientation, displaySize);
+ updateResources(orientation, displaySize);
mExpandedViewPadding = expandedViewPadding;
}
@@ -168,7 +168,7 @@
* @param orientation Landscape or portrait.
* @param displaySize Updated display size.
*/
- public void updateOrientation(int orientation, Point displaySize) {
+ public void updateResources(int orientation, Point displaySize) {
mScreenOrientation = orientation;
mDisplaySize = displaySize;
if (mLayout != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 5f3a2bd..2cfe1dd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -809,7 +809,7 @@
* Update effective screen width based on current orientation.
* @param orientation Landscape or portrait.
*/
- public void updateOrientation(int orientation) {
+ public void updateResources(int orientation) {
if (mLayout != null) {
Resources res = mLayout.getContext().getResources();
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 79dd9ed..4b283d6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -23,9 +23,14 @@
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.view.accessibility.AccessibilityNodeInfo
import android.widget.CheckBox
import android.widget.ImageView
+import android.widget.Switch
import android.widget.TextView
+import androidx.core.view.AccessibilityDelegateCompat
+import androidx.core.view.ViewCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R
@@ -72,7 +77,8 @@
elevation = this@ControlAdapter.elevation
background = parent.context.getDrawable(
R.drawable.control_background_ripple)
- }
+ },
+ model is FavoritesModel // Indicates that position information is needed
) { id, favorite ->
model?.changeFavoriteStatus(id, favorite)
}
@@ -175,8 +181,14 @@
*/
internal class ControlHolder(
view: View,
+ val withPosition: Boolean,
val favoriteCallback: ModelFavoriteChanger
) : Holder(view) {
+ private val favoriteStateDescription =
+ itemView.context.getString(R.string.accessibility_control_favorite)
+ private val notFavoriteStateDescription =
+ itemView.context.getString(R.string.accessibility_control_not_favorite)
+
private val icon: ImageView = itemView.requireViewById(R.id.icon)
private val title: TextView = itemView.requireViewById(R.id.title)
private val subtitle: TextView = itemView.requireViewById(R.id.subtitle)
@@ -185,15 +197,38 @@
visibility = View.VISIBLE
}
+ private val accessibilityDelegate = ControlHolderAccessibilityDelegate(this::stateDescription)
+
+ init {
+ ViewCompat.setAccessibilityDelegate(itemView, accessibilityDelegate)
+ }
+
+ // Determine the stateDescription based on favorite state and maybe position
+ private fun stateDescription(favorite: Boolean): CharSequence? {
+ if (!favorite) {
+ return notFavoriteStateDescription
+ } else if (!withPosition) {
+ return favoriteStateDescription
+ } else {
+ val position = layoutPosition + 1
+ return itemView.context.getString(
+ R.string.accessibility_control_favorite_position, position)
+ }
+ }
+
override fun bindData(wrapper: ElementWrapper) {
wrapper as ControlInterface
val renderInfo = getRenderInfo(wrapper.component, wrapper.deviceType)
title.text = wrapper.title
subtitle.text = wrapper.subtitle
- favorite.isChecked = wrapper.favorite
- removed.text = if (wrapper.removed) "Removed" else ""
+ updateFavorite(wrapper.favorite)
+ removed.text = if (wrapper.removed) {
+ itemView.context.getText(R.string.controls_removed)
+ } else {
+ ""
+ }
itemView.setOnClickListener {
- favorite.isChecked = !favorite.isChecked
+ updateFavorite(!favorite.isChecked)
favoriteCallback(wrapper.controlId, favorite.isChecked)
}
applyRenderInfo(renderInfo)
@@ -201,6 +236,8 @@
override fun updateFavorite(favorite: Boolean) {
this.favorite.isChecked = favorite
+ accessibilityDelegate.isFavorite = favorite
+ itemView.stateDescription = stateDescription(favorite)
}
private fun getRenderInfo(
@@ -219,6 +256,36 @@
}
}
+private class ControlHolderAccessibilityDelegate(
+ val stateRetriever: (Boolean) -> CharSequence?
+) : AccessibilityDelegateCompat() {
+
+ var isFavorite = false
+
+ override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+
+ // Change the text for the double-tap action
+ val clickActionString = if (isFavorite) {
+ host.context.getString(R.string.accessibility_control_change_unfavorite)
+ } else {
+ host.context.getString(R.string.accessibility_control_change_favorite)
+ }
+ val click = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ AccessibilityNodeInfo.ACTION_CLICK,
+ // “favorite/unfavorite”
+ clickActionString)
+ info.addAction(click)
+
+ // Determine the stateDescription based on the holder information
+ info.stateDescription = stateRetriever(isFavorite)
+ // Remove the information at the end indicating row and column.
+ info.setCollectionItemInfo(null)
+
+ info.className = Switch::class.java.name
+ }
+}
+
class MarginItemDecorator(
private val topMargin: Int,
private val sideMargins: Int
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 4e9c550..3a4e82c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -191,7 +191,18 @@
recyclerView.apply {
this.adapter = adapter
- layoutManager = GridLayoutManager(recyclerView.context, 2).apply {
+ layoutManager = object : GridLayoutManager(recyclerView.context, 2) {
+
+ // This will remove from the announcement the row corresponding to the divider,
+ // as it's not something that should be announced.
+ override fun getRowCountForAccessibility(
+ recycler: RecyclerView.Recycler,
+ state: RecyclerView.State
+ ): Int {
+ val initial = super.getRowCountForAccessibility(recycler, state)
+ return if (initial > 0) initial - 1 else initial
+ }
+ }.apply {
spanSizeLookup = adapter.spanSizeLookup
}
addItemDecoration(itemDecorator)
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 3f37861..f93c169 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -69,6 +69,9 @@
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.transition.AutoTransition;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
import android.util.ArraySet;
import android.util.FeatureFlagUtils;
import android.util.Log;
@@ -206,7 +209,8 @@
@VisibleForTesting
protected final ArrayList<Action> mOverflowItems = new ArrayList<>();
- private ActionsDialog mDialog;
+ @VisibleForTesting
+ protected ActionsDialog mDialog;
private Action mSilentModeAction;
private ToggleAction mAirplaneModeOn;
@@ -246,6 +250,9 @@
@UiEvent(doc = "The global actions / power menu surface became visible on the screen.")
GA_POWER_MENU_OPEN(337),
+ @UiEvent(doc = "The global actions / power menu surface was dismissed.")
+ GA_POWER_MENU_CLOSE(471),
+
@UiEvent(doc = "The global actions bugreport button was pressed.")
GA_BUGREPORT_PRESS(344),
@@ -360,10 +367,9 @@
@Override
public void onUnlockedChanged() {
if (mDialog != null) {
- boolean unlocked = keyguardStateController.isUnlocked()
- || keyguardStateController.canDismissLockScreen();
if (mDialog.mPanelController != null) {
- mDialog.mPanelController.onDeviceLockStateChanged(unlocked);
+ mDialog.mPanelController.onDeviceLockStateChanged(
+ !mKeyguardStateController.isUnlocked());
}
if (!mDialog.isShowingControls() && shouldShowControls()) {
mDialog.showControls(mControlsUiController);
@@ -1168,6 +1174,7 @@
if (mDialog == dialog) {
mDialog = null;
}
+ mUiEventLogger.log(GlobalActionsEvent.GA_POWER_MENU_CLOSE);
mWindowManagerFuncs.onGlobalActionsHidden();
mLifecycle.setCurrentState(Lifecycle.State.DESTROYED);
}
@@ -2022,7 +2029,22 @@
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
- panelContainer.addView(mPanelController.getPanelContent(), panelParams);
+ View walletView = mPanelController.getPanelContent();
+ panelContainer.addView(walletView, panelParams);
+ // Smooth transitions when wallet is resized, which can happen when a card is added
+ ViewGroup root = findViewById(com.android.systemui.R.id.global_actions_grid_root);
+ if (root != null) {
+ walletView.addOnLayoutChangeListener((v, l, t, r, b, ol, ot, or, ob) -> {
+ int oldHeight = ob - ot;
+ int newHeight = b - t;
+ if (oldHeight > 0 && oldHeight != newHeight) {
+ TransitionSet transition = new AutoTransition()
+ .setDuration(250)
+ .setOrdering(TransitionSet.ORDERING_TOGETHER);
+ TransitionManager.beginDelayedTransition(root, transition);
+ }
+ });
+ }
}
}
@@ -2378,9 +2400,7 @@
@VisibleForTesting
protected boolean shouldShowControls() {
- boolean isUnlocked = mKeyguardStateController.isUnlocked()
- || mKeyguardStateController.canDismissLockScreen();
- return (isUnlocked || mShowLockScreenCardsAndControls)
+ return (mKeyguardStateController.isUnlocked() || mShowLockScreenCardsAndControls)
&& mControlsUiController.getAvailable()
&& !mControlsServiceInfos.isEmpty()
&& mDeviceProvisioned;
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
index fa45ea1..1a53c28 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
@@ -134,7 +134,7 @@
private void setupTexture(Bitmap bitmap) {
final int[] tids = new int[1];
- if (bitmap == null) {
+ if (bitmap == null || bitmap.isRecycled()) {
Log.w(TAG, "setupTexture: invalid bitmap");
return;
}
@@ -146,16 +146,20 @@
return;
}
- // Bind a named texture to a target.
- glBindTexture(GL_TEXTURE_2D, tids[0]);
- // Load the bitmap data and copy it over into the texture object that is currently bound.
- GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
- // Use bilinear texture filtering when minification.
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- // Use bilinear texture filtering when magnification.
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- mTextureId = tids[0];
+ try {
+ // Bind a named texture to a target.
+ glBindTexture(GL_TEXTURE_2D, tids[0]);
+ // Load the bitmap data and copy it over into the texture object
+ // that is currently bound.
+ GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
+ // Use bilinear texture filtering when minification.
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ // Use bilinear texture filtering when magnification.
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ mTextureId = tids[0];
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Failed uploading texture: " + e.getLocalizedMessage());
+ }
}
void useTexture() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index c3a7d9f..e08d7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -26,6 +26,7 @@
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
@@ -98,7 +99,6 @@
private PlayerViewHolder mViewHolder;
private MediaSession.Token mToken;
private MediaController mController;
- private int mForegroundColor;
private int mBackgroundColor;
private MediaDevice mDevice;
protected ComponentName mServiceComponent;
@@ -243,7 +243,6 @@
return;
}
MediaSession.Token token = data.getToken();
- mForegroundColor = data.getForegroundColor();
mBackgroundColor = data.getBackgroundColor();
if (mToken == null || !mToken.equals(token)) {
if (mQSMediaBrowser != null) {
@@ -304,24 +303,19 @@
// App icon
ImageView appIcon = mViewHolder.getAppIcon();
- Drawable iconDrawable = data.getAppIcon().mutate();
- iconDrawable.setTint(mForegroundColor);
- appIcon.setImageDrawable(iconDrawable);
+ appIcon.setImageDrawable(data.getAppIcon());
// Song name
TextView titleText = mViewHolder.getTitleText();
titleText.setText(data.getSong());
- titleText.setTextColor(data.getForegroundColor());
// App title
TextView appName = mViewHolder.getAppName();
appName.setText(data.getApp());
- appName.setTextColor(mForegroundColor);
// Artist name
TextView artistText = mViewHolder.getArtistText();
artistText.setText(data.getArtist());
- artistText.setTextColor(mForegroundColor);
// Transfer chip
if (mLocalMediaManager != null) {
@@ -329,6 +323,10 @@
setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */);
setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */);
updateDevice(mLocalMediaManager.getCurrentConnectedDevice());
+ if (mViewHolder.getBackground().getBackground() instanceof IlluminationDrawable) {
+ ((IlluminationDrawable) mViewHolder.getBackground().getBackground())
+ .setupTouch(mViewHolder.getSeamless(), mViewHolder.getPlayer());
+ }
mViewHolder.getSeamless().setOnClickListener(v -> {
final Intent intent = new Intent()
.setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
@@ -358,7 +356,6 @@
MediaAction mediaAction = actionIcons.get(i);
button.setImageDrawable(mediaAction.getDrawable());
button.setContentDescription(mediaAction.getContentDescription());
- button.setImageTintList(ColorStateList.valueOf(mForegroundColor));
PendingIntent actionIntent = mediaAction.getIntent();
if (mViewHolder.getBackground().getBackground() instanceof IlluminationDrawable) {
@@ -389,8 +386,7 @@
// Seek Bar
final MediaController controller = getController();
- mBackgroundExecutor.execute(
- () -> mSeekBarViewModel.updateController(controller, data.getForegroundColor()));
+ mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
// Set up long press menu
// TODO: b/156036025 bring back media guts
@@ -541,32 +537,27 @@
if (mViewHolder == null) {
return;
}
- ColorStateList fgTintList = ColorStateList.valueOf(mForegroundColor);
+ ImageView iconView = mViewHolder.getSeamlessIcon();
+ TextView deviceName = mViewHolder.getSeamlessText();
// Update the outline color
LinearLayout viewLayout = (LinearLayout) mViewHolder.getSeamless();
RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground();
GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0);
- rect.setStroke(2, mForegroundColor);
- rect.setColor(mBackgroundColor);
-
- ImageView iconView = mViewHolder.getSeamlessIcon();
- TextView deviceName = mViewHolder.getSeamlessText();
- deviceName.setTextColor(fgTintList);
+ rect.setStroke(2, deviceName.getCurrentTextColor());
+ rect.setColor(Color.TRANSPARENT);
if (mIsRemotePlayback) {
mViewHolder.getSeamless().setEnabled(false);
mViewHolder.getSeamless().setAlpha(0.38f);
iconView.setImageResource(R.drawable.ic_hardware_speaker);
iconView.setVisibility(View.VISIBLE);
- iconView.setImageTintList(fgTintList);
deviceName.setText(R.string.media_seamless_remote_device);
} else if (device != null) {
mViewHolder.getSeamless().setEnabled(true);
mViewHolder.getSeamless().setAlpha(1f);
Drawable icon = device.getIcon();
iconView.setVisibility(View.VISIBLE);
- iconView.setImageTintList(fgTintList);
if (icon instanceof AdaptiveIcon) {
AdaptiveIcon aIcon = (AdaptiveIcon) icon;
@@ -639,7 +630,6 @@
mQSMediaBrowser.restart();
});
btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play));
- btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
setVisibleAndAlpha(expandedSet, ACTION_IDS[0], true /*visible */);
setVisibleAndAlpha(collapsedSet, ACTION_IDS[0], true /*visible */);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 6a26461..85965d0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -24,7 +24,6 @@
/** State of a media view. */
data class MediaData(
val initialized: Boolean = false,
- val foregroundColor: Int,
val backgroundColor: Int,
val app: String?,
val appIcon: Drawable?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 8da864c..07ce9e4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -21,6 +21,7 @@
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
+import android.graphics.Color
import android.graphics.ImageDecoder
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
@@ -30,7 +31,7 @@
import android.service.notification.StatusBarNotification
import android.text.TextUtils
import android.util.Log
-import com.android.internal.util.ContrastColorUtil
+import com.android.internal.graphics.ColorUtils
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.notification.MediaNotificationProcessor
@@ -40,7 +41,6 @@
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
-import kotlin.collections.LinkedHashMap
// URI fields to try loading album art from
private val ART_URIS = arrayOf(
@@ -50,8 +50,11 @@
)
private const val TAG = "MediaDataManager"
+private const val DEFAULT_LUMINOSITY = 0.25f
+private const val LUMINOSITY_THRESHOLD = 0.05f
+private const val SATURATION_MULTIPLIER = 0.8f
-private val LOADING = MediaData(false, 0, 0, null, null, null, null, null,
+private val LOADING = MediaData(false, 0, null, null, null, null, null,
emptyList(), emptyList(), null, null, null)
/**
@@ -107,8 +110,7 @@
// Foreground and Background colors computed from album art
val notif: Notification = sbn.notification
- var fgColor = notif.color
- var bgColor = -1
+ var bgColor = Color.WHITE
var artworkBitmap = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART)
if (artworkBitmap == null) {
artworkBitmap = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
@@ -142,13 +144,22 @@
.generate()
val swatch = MediaNotificationProcessor.findBackgroundSwatch(p)
bgColor = swatch.rgb
- fgColor = MediaNotificationProcessor.selectForegroundColor(bgColor, p)
}
- // Make sure colors will be legible
- val isDark = !ContrastColorUtil.isColorLight(bgColor)
- fgColor = ContrastColorUtil.resolveContrastColor(context, fgColor, bgColor,
- isDark)
- fgColor = ContrastColorUtil.ensureTextContrast(fgColor, bgColor, isDark)
+ // Adapt background color, so it's always subdued and text is legible
+ val tmpHsl = floatArrayOf(0f, 0f, 0f)
+ ColorUtils.colorToHSL(bgColor, tmpHsl)
+
+ val l = tmpHsl[2]
+ // Colors with very low luminosity can have any saturation. This means that changing the
+ // luminosity can make a black become red. Let's remove the saturation of very light or
+ // very dark colors to avoid this issue.
+ if (l < LUMINOSITY_THRESHOLD || l > 1f - LUMINOSITY_THRESHOLD) {
+ tmpHsl[1] = 0f
+ }
+ tmpHsl[1] *= SATURATION_MULTIPLIER
+ tmpHsl[2] = DEFAULT_LUMINOSITY
+
+ bgColor = ColorUtils.HSLToColor(tmpHsl)
// App name
val builder = Notification.Builder.recoverBuilder(context, notif)
@@ -189,8 +200,8 @@
}
foregroundExcecutor.execute {
- onMediaDataLoaded(key, MediaData(true, fgColor, bgColor, app, smallIconDrawable, artist,
- song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token,
+ onMediaDataLoaded(key, MediaData(true, bgColor, app, smallIconDrawable, artist, song,
+ artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token,
notif.contentIntent))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
index 9c92b0a..d72c369 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
@@ -42,10 +42,10 @@
val mediaCarousel: HorizontalScrollView
private val mediaContent: ViewGroup
private val mediaPlayers: MutableMap<String, MediaControlPanel> = mutableMapOf()
- private val visualStabilityCallback = ::reorderAllPlayers
+ 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) {
@@ -75,6 +75,16 @@
mediaCarousel = inflateMediaCarousel()
mediaCarousel.setOnScrollChangeListener(scrollChangedListener)
mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
+ visualStabilityCallback = VisualStabilityManager.Callback {
+ if (needsReordering) {
+ needsReordering = false
+ reorderAllPlayers()
+ }
+ // Let's reset our scroll position
+ mediaCarousel.scrollX = 0
+ }
+ visualStabilityManager.addReorderingAllowedCallback(visualStabilityCallback,
+ true /* persistent */)
mediaManager.addListener(object : MediaDataManager.Listener {
override fun onMediaDataLoaded(key: String, data: MediaData) {
updateView(key, data)
@@ -169,7 +179,7 @@
mediaContent.removeView(existingPlayer.view?.player)
mediaContent.addView(existingPlayer.view?.player, 0)
} else {
- visualStabilityManager.addReorderingAllowedCallback(visualStabilityCallback)
+ needsReordering = true
}
}
existingPlayer.bind(data)
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index 51c157a..110b08c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media
import android.content.res.ColorStateList
+import android.graphics.Color
import android.text.format.DateUtils
import android.view.View
import android.widget.SeekBar
@@ -46,18 +47,6 @@
/** Updates seek bar views when the data model changes. */
@UiThread
override fun onChanged(data: SeekBarViewModel.Progress) {
-
- data.color?.let {
- var tintList = ColorStateList.valueOf(it)
- seekBarView.setThumbTintList(tintList)
- tintList = tintList.withAlpha(192) // 75%
- seekBarView.setProgressTintList(tintList)
- tintList = tintList.withAlpha(128) // 50%
- seekBarView.setProgressBackgroundTintList(tintList)
- elapsedTimeView.setTextColor(it)
- totalTimeView.setTextColor(it)
- }
-
if (!data.enabled) {
seekBarView.setEnabled(false)
seekBarView.getThumb().setAlpha(0)
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index f72a74b..b08124b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -67,7 +67,7 @@
/** ViewModel for seek bar in QS media player. */
class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
- private var _data = Progress(false, false, null, null, null)
+ private var _data = Progress(false, false, null, null)
set(value) {
field = value
_progress.postValue(value)
@@ -100,10 +100,9 @@
/**
* Updates media information.
* @param mediaController controller for media session
- * @param color foreground color for UI elements
*/
@WorkerThread
- fun updateController(mediaController: MediaController?, color: Int) {
+ fun updateController(mediaController: MediaController?) {
controller = mediaController
playbackState = controller?.playbackState
val mediaMetadata = controller?.metadata
@@ -113,7 +112,7 @@
val enabled = if (playbackState == null ||
playbackState?.getState() == PlaybackState.STATE_NONE ||
(duration != null && duration <= 0)) false else true
- _data = Progress(enabled, seekAvailable, position, duration, color)
+ _data = Progress(enabled, seekAvailable, position, duration)
if (shouldPollPlaybackPosition()) {
checkPlaybackPosition()
}
@@ -191,7 +190,6 @@
val enabled: Boolean,
val seekAvailable: Boolean,
val elapsedTime: Int?,
- val duration: Int?,
- val color: Int?
+ val duration: Int?
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index f72de11..13516a9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -65,6 +65,10 @@
@Retention(RetentionPolicy.SOURCE)
public @interface TransitionDirection {}
+ public static boolean isInPipDirection(@TransitionDirection int direction) {
+ return direction == TRANSITION_DIRECTION_TO_PIP;
+ }
+
public static boolean isOutPipDirection(@TransitionDirection int direction) {
return direction == TRANSITION_DIRECTION_TO_FULLSCREEN
|| direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN;
@@ -104,6 +108,12 @@
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
+ } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
+ && mCurrentAnimator.isRunning()) {
+ // If we are still animating the fade into pip, then just move the surface and ensure
+ // we update with the new destination bounds, but don't interrupt the existing animation
+ // with a new bounds
+ mCurrentAnimator.setDestinationBounds(endBounds);
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
&& mCurrentAnimator.isRunning()) {
mCurrentAnimator.setDestinationBounds(endBounds);
@@ -265,8 +275,7 @@
boolean inScaleTransition() {
if (mAnimationType != ANIM_TYPE_BOUNDS) return false;
- final int direction = getTransitionDirection();
- return !isOutPipDirection(direction) && direction != TRANSITION_DIRECTION_TO_PIP;
+ return !isInPipDirection(getTransitionDirection());
}
/**
@@ -354,7 +363,11 @@
getCastedFractionValue(start.bottom, end.bottom, fraction));
setCurrentValue(mTmpRect);
if (inScaleTransition()) {
- getSurfaceTransactionHelper().scale(tx, leash, start, mTmpRect);
+ if (isOutPipDirection(getTransitionDirection())) {
+ getSurfaceTransactionHelper().scale(tx, leash, end, mTmpRect);
+ } else {
+ getSurfaceTransactionHelper().scale(tx, leash, start, mTmpRect);
+ }
} else {
getSurfaceTransactionHelper().crop(tx, leash, mTmpRect);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 3eeadc3..394f997 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -209,6 +209,10 @@
return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
}
+ public int getDisplayRotation() {
+ return mDisplayInfo.rotation;
+ }
+
/**
* Responds to IPinnedStackListener on {@link DisplayInfo} change.
* It will normally follow up with a
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 3e6b98f..bb627bc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -16,6 +16,9 @@
package com.android.systemui.pip;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
@@ -25,25 +28,30 @@
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_SPLIT_SCREEN;
+import static com.android.systemui.pip.PipAnimationController.isInPipDirection;
import static com.android.systemui.pip.PipAnimationController.isOutPipDirection;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
import android.util.Log;
import android.util.Size;
import android.view.SurfaceControl;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
import android.window.WindowOrganizer;
import com.android.internal.os.SomeArgs;
@@ -51,6 +59,7 @@
import com.android.systemui.pip.phone.PipUpdateThread;
import com.android.systemui.stackdivider.Divider;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -90,7 +99,7 @@
private final Rect mLastReportedBounds = new Rect();
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>();
+ private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
private final Divider mSplitDivider;
// These callbacks are called on the update thread
@@ -110,7 +119,8 @@
@Override
public void onPipAnimationEnd(SurfaceControl.Transaction tx,
PipAnimationController.PipTransitionAnimator animator) {
- finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection());
+ finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection(),
+ animator.getAnimationType());
mMainHandler.post(() -> {
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
@@ -170,7 +180,7 @@
case MSG_FINISH_RESIZE: {
SurfaceControl.Transaction tx = (SurfaceControl.Transaction) args.arg2;
Rect toBounds = (Rect) args.arg3;
- finishResize(tx, toBounds, args.argi1 /* direction */);
+ finishResize(tx, toBounds, args.argi1 /* direction */, -1);
if (updateBoundsCallback != null) {
updateBoundsCallback.accept(toBounds);
}
@@ -240,28 +250,76 @@
}
/**
- * Dismiss PiP, this is done in two phases using {@link WindowContainerTransaction}
- * - setActivityWindowingMode to undefined at beginning of the transaction. without changing
- * the windowing mode of the Task itself. This makes sure the activity render it's final
- * configuration while the Task is still in PiP.
+ * Expands PiP to the previous bounds, this is done in two phases using
+ * {@link WindowContainerTransaction}
+ * - setActivityWindowingMode to either fullscreen or split-secondary at beginning of the
+ * transaction. without changing the windowing mode of the Task itself. This makes sure the
+ * activity render it's final configuration while the Task is still in PiP.
* - setWindowingMode to undefined at the end of transition
* @param animationDurationMs duration in millisecond for the exiting PiP transition
*/
- public void dismissPip(int animationDurationMs) {
+ public void exitPip(int animationDurationMs) {
if (!mInPip || mToken == null) {
- Log.wtf(TAG, "Not allowed to dismissPip in current state"
+ Log.wtf(TAG, "Not allowed to exitPip in current state"
+ " mInPip=" + mInPip + " mToken=" + mToken);
return;
}
+
+ final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
+ final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
+ != mPipBoundsHandler.getDisplayRotation();
final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
- WindowOrganizer.applyTransaction(wct);
- final Rect destinationBounds = mBoundsToRestore.remove(mToken.asBinder());
- final int direction = syncWithSplitScreenBounds(destinationBounds)
- ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN;
- scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
- direction, animationDurationMs, null /* updateBoundsCallback */);
- mInPip = false;
+ if (orientationDiffers) {
+ // Don't bother doing an animation if the display rotation differs or if it's in
+ // a non-supported windowing mode
+ wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ WindowOrganizer.applyTransaction(wct);
+ mInPip = false;
+ } else {
+ final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
+ final int direction = syncWithSplitScreenBounds(destinationBounds)
+ ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN
+ : TRANSITION_DIRECTION_TO_FULLSCREEN;
+ final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
+ mLastReportedBounds);
+ tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
+ wct.setActivityWindowingMode(mToken, direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN
+ ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ : WINDOWING_MODE_FULLSCREEN);
+ wct.setBounds(mToken, destinationBounds);
+ wct.setBoundsChangeTransaction(mToken, tx);
+ applySyncTransaction(wct, new WindowContainerTransactionCallback() {
+ @Override
+ public void onTransactionReady(int id, SurfaceControl.Transaction t) {
+ t.apply();
+ scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
+ direction, animationDurationMs, null /* updateBoundsCallback */);
+ mInPip = false;
+ }
+ });
+ }
+ }
+
+ /**
+ * Removes PiP immediately.
+ */
+ public void removePip() {
+ if (!mInPip || mToken == null) {
+ Log.wtf(TAG, "Not allowed to removePip in current state"
+ + " mInPip=" + mInPip + " mToken=" + mToken);
+ return;
+ }
+ getUpdateHandler().post(() -> {
+ try {
+ ActivityTaskManager.getService().removeStacksInWindowingModes(
+ new int[]{ WINDOWING_MODE_PINNED });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to remove PiP", e);
+ }
+ });
+ mInitialState.remove(mToken.asBinder());
}
@Override
@@ -277,19 +335,37 @@
mInPip = true;
mLeash = leash;
+ // TODO: Skip enter animation when entering pip from another orientation
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
- mBoundsToRestore.put(mToken.asBinder(), currentBounds);
+ mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
+
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
scheduleAnimateResizePip(currentBounds, destinationBounds,
TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
null /* updateBoundsCallback */);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
- mUpdateHandler.post(() -> mPipAnimationController
- .getAnimator(mLeash, destinationBounds, 0f, 1f)
- .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
- .setPipAnimationCallback(mPipAnimationCallback)
- .setDuration(mEnterExitAnimationDuration)
- .start());
+ // If we are fading the PIP in, then we should move the pip to the final location as
+ // soon as possible, but set the alpha immediately since the transaction can take a
+ // while to process
+ final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ tx.setAlpha(mLeash, 0f);
+ tx.apply();
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ wct.setBounds(mToken, destinationBounds);
+ wct.scheduleFinishEnterPip(mToken, destinationBounds);
+ applySyncTransaction(wct, new WindowContainerTransactionCallback() {
+ @Override
+ public void onTransactionReady(int id, SurfaceControl.Transaction t) {
+ t.apply();
+ mUpdateHandler.post(() -> mPipAnimationController
+ .getAnimator(mLeash, destinationBounds, 0f, 1f)
+ .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(mEnterExitAnimationDuration)
+ .start());
+ }
+ });
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
} else {
throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType);
@@ -297,7 +373,7 @@
}
/**
- * Note that dismissing PiP is now originated from SystemUI, see {@link #dismissPip(int)}.
+ * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
* Meanwhile this callback is invoked whenever the task is removed. For instance:
* - as a result of removeStacksInWindowingModes from WM
* - activity itself is died
@@ -411,6 +487,7 @@
// can be initiated in other component, ignore if we are no longer in PIP
return;
}
+
SomeArgs args = SomeArgs.obtain();
args.arg1 = updateBoundsCallback;
args.arg2 = currentBounds;
@@ -518,7 +595,7 @@
throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
+ "directly");
}
- // Could happen when dismissPip
+ // Could happen when exitPip
if (mToken == null || mLeash == null) {
Log.w(TAG, "Abort animation, invalid leash");
return;
@@ -535,7 +612,7 @@
throw new RuntimeException("Callers should call scheduleUserResizePip() instead of "
+ "this directly");
}
- // Could happen when dismissPip
+ // Could happen when exitPip
if (mToken == null || mLeash == null) {
Log.w(TAG, "Abort animation, invalid leash");
return;
@@ -546,32 +623,43 @@
}
private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
- @PipAnimationController.TransitionDirection int direction) {
+ @PipAnimationController.TransitionDirection int direction,
+ @PipAnimationController.AnimationType int type) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
+ "directly");
}
mLastReportedBounds.set(destinationBounds);
+ if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
+ return;
+ }
+
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect taskBounds;
- if (isOutPipDirection(direction)) {
+ if (isInPipDirection(direction)) {
+ // If we are animating from fullscreen using a bounds animation, then reset the
+ // activity windowing mode set by WM, and set the task bounds to the final bounds
+ taskBounds = destinationBounds;
+ wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ wct.scheduleFinishEnterPip(mToken, destinationBounds);
+ } else if (isOutPipDirection(direction)) {
// If we are animating to fullscreen, then we need to reset the override bounds
// on the task to ensure that the task "matches" the parent's bounds.
taskBounds = (direction == TRANSITION_DIRECTION_TO_FULLSCREEN)
? null : destinationBounds;
- // As for the final windowing mode, simply reset it to undefined.
+ // As for the final windowing mode, simply reset it to undefined and reset the activity
+ // mode set prior to the animation running
wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
if (mSplitDivider != null && direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN) {
wct.reparent(mToken, mSplitDivider.getSecondaryRoot(), true /* onTop */);
}
} else {
+ // Just a resize in PIP
taskBounds = destinationBounds;
}
- if (direction == TRANSITION_DIRECTION_TO_PIP) {
- wct.scheduleFinishEnterPip(mToken, taskBounds);
- } else {
- wct.setBounds(mToken, taskBounds);
- }
+
+ wct.setBounds(mToken, taskBounds);
wct.setBoundsChangeTransaction(mToken, tx);
WindowOrganizer.applyTransaction(wct);
}
@@ -582,17 +670,17 @@
throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
+ "this directly");
}
- // Could happen when dismissPip
+ // Could happen when exitPip
if (mToken == null || mLeash == null) {
Log.w(TAG, "Abort animation, invalid leash");
return;
}
- mUpdateHandler.post(() -> mPipAnimationController
+ mPipAnimationController
.getAnimator(mLeash, currentBounds, destinationBounds)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
- .start());
+ .start();
}
private Size getMinimalSize(ActivityInfo activityInfo) {
@@ -633,6 +721,27 @@
}
/**
+ * Dumps internal states.
+ */
+ public void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + "mTaskInfo=" + mTaskInfo);
+ pw.println(innerPrefix + "mToken=" + mToken
+ + " binder=" + (mToken != null ? mToken.asBinder() : null));
+ pw.println(innerPrefix + "mLeash=" + mLeash);
+ pw.println(innerPrefix + "mInPip=" + mInPip);
+ pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
+ pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
+ pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds);
+ pw.println(innerPrefix + "mInitialState:");
+ for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
+ pw.println(innerPrefix + " binder=" + e.getKey()
+ + " winConfig=" + e.getValue().windowConfiguration);
+ }
+ }
+
+ /**
* Callback interface for PiP transitions (both from and to PiP mode)
*/
public interface PipTransitionCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java
index 84f7e89..f1eef43 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java
@@ -101,7 +101,7 @@
result = true;
break;
case AccessibilityNodeInfo.ACTION_EXPAND:
- mMotionHelper.expandPip();
+ mMotionHelper.expandPipToFullscreen();
result = true;
break;
default:
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 0841bb7..30d6cd9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -25,7 +25,6 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityManager;
-import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -140,7 +139,7 @@
!= WINDOWING_MODE_PINNED) {
return;
}
- mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */);
+ mTouchHandler.getMotionHelper().expandPipToFullscreen(clearedTask /* skipAnimation */);
}
};
@@ -214,7 +213,6 @@
}
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
- final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
mPipBoundsHandler = pipBoundsHandler;
mPipTaskOrganizer = pipTaskOrganizer;
mPipTaskOrganizer.registerPipTransitionCallback(this);
@@ -222,7 +220,7 @@
mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
mMenuController = new PipMenuActivityController(context, mMediaController,
mInputConsumerController);
- mTouchHandler = new PipTouchHandler(context, mActivityManager, activityTaskManager,
+ mTouchHandler = new PipTouchHandler(context, mActivityManager,
mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
floatingContentCoordinator, deviceConfig, pipSnapAlgorithm);
mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
@@ -237,7 +235,7 @@
try {
mPipTaskOrganizer.registerOrganizer(WINDOWING_MODE_PINNED);
- ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo(
+ ActivityManager.StackInfo stackInfo = ActivityTaskManager.getService().getStackInfo(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stackInfo != null) {
// If SystemUI restart, and it already existed a pinned stack,
@@ -261,7 +259,7 @@
*/
@Override
public void expandPip() {
- mTouchHandler.getMotionHelper().expandPip(false /* skipAnimation */);
+ mTouchHandler.getMotionHelper().expandPipToFullscreen(false /* skipAnimation */);
}
/**
@@ -378,5 +376,6 @@
mMenuController.dump(pw, innerPrefix);
mTouchHandler.dump(pw, innerPrefix);
mPipBoundsHandler.dump(pw, innerPrefix);
+ mPipTaskOrganizer.dump(pw, innerPrefix);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 08d4222..d099597 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -16,16 +16,12 @@
package com.android.systemui.pip.phone;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
import android.os.Debug;
-import android.os.RemoteException;
import android.util.Log;
import androidx.dynamicanimation.animation.SpringForce;
@@ -33,7 +29,6 @@
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.animation.FloatProperties;
import com.android.systemui.util.animation.PhysicsAnimator;
@@ -60,12 +55,10 @@
private static final float DEFAULT_FRICTION = 2f;
private final Context mContext;
- private final IActivityTaskManager mActivityTaskManager;
private final PipTaskOrganizer mPipTaskOrganizer;
private PipMenuActivityController mMenuController;
private PipSnapAlgorithm mSnapAlgorithm;
- private FlingAnimationUtils mFlingAnimationUtils;
private final Rect mStableInsets = new Rect();
@@ -147,16 +140,13 @@
public void onPipTransitionCanceled(ComponentName activity, int direction) {}
};
- public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
- PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
- PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
+ public PipMotionHelper(Context context, PipTaskOrganizer pipTaskOrganizer,
+ PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm,
FloatingContentCoordinator floatingContentCoordinator) {
mContext = context;
- mActivityTaskManager = activityTaskManager;
mPipTaskOrganizer = pipTaskOrganizer;
mMenuController = menuController;
mSnapAlgorithm = snapAlgorithm;
- mFlingAnimationUtils = flingAnimationUtils;
mFloatingContentCoordinator = floatingContentCoordinator;
onConfigurationChanged();
mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
@@ -267,22 +257,24 @@
/**
* Resizes the pinned stack back to fullscreen.
*/
- void expandPip() {
- expandPip(false /* skipAnimation */);
+ void expandPipToFullscreen() {
+ expandPipToFullscreen(false /* skipAnimation */);
}
/**
* Resizes the pinned stack back to fullscreen.
*/
- void expandPip(boolean skipAnimation) {
+ void expandPipToFullscreen(boolean skipAnimation) {
if (DEBUG) {
- Log.d(TAG, "expandPip: skipAnimation=" + skipAnimation
+ Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation
+ " callers=\n" + Debug.getCallers(5, " "));
}
cancelAnimations();
mMenuController.hideMenuWithoutResize();
mPipTaskOrganizer.getUpdateHandler().post(() -> {
- mPipTaskOrganizer.dismissPip(skipAnimation ? 0 : EXPAND_STACK_TO_FULLSCREEN_DURATION);
+ mPipTaskOrganizer.exitPip(skipAnimation
+ ? 0
+ : EXPAND_STACK_TO_FULLSCREEN_DURATION);
});
}
@@ -292,18 +284,11 @@
@Override
public void dismissPip() {
if (DEBUG) {
- Log.d(TAG, "dismissPip: callers=\n" + Debug.getCallers(5, " "));
+ Log.d(TAG, "removePip: callers=\n" + Debug.getCallers(5, " "));
}
cancelAnimations();
mMenuController.hideMenuWithoutResize();
- mPipTaskOrganizer.getUpdateHandler().post(() -> {
- try {
- mActivityTaskManager.removeStacksInWindowingModes(
- new int[]{ WINDOWING_MODE_PINNED });
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to remove PiP", e);
- }
- });
+ mPipTaskOrganizer.removePip();
}
/** Sets the movement bounds to use to constrain PIP position animations. */
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 0468373..59c962d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -23,7 +23,6 @@
import android.annotation.SuppressLint;
import android.app.IActivityManager;
-import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -188,7 +187,7 @@
@Override
public void onPipExpand() {
- mMotionHelper.expandPip();
+ mMotionHelper.expandPipToFullscreen();
}
@Override
@@ -210,7 +209,7 @@
@SuppressLint("InflateParams")
public PipTouchHandler(Context context, IActivityManager activityManager,
- IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
+ PipMenuActivityController menuController,
InputConsumerController inputConsumerController,
PipBoundsHandler pipBoundsHandler,
PipTaskOrganizer pipTaskOrganizer,
@@ -228,8 +227,8 @@
mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(),
2.5f);
mGesture = new DefaultPipTouchGesture();
- mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer,
- mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
+ mMotionHelper = new PipMotionHelper(mContext, pipTaskOrganizer, mMenuController,
+ mSnapAlgorithm, floatingContentCoordinator);
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
deviceConfig, pipTaskOrganizer, this::getMovementBounds,
@@ -815,6 +814,7 @@
private class DefaultPipTouchGesture extends PipTouchGesture {
private final Point mStartPosition = new Point();
private final PointF mDelta = new PointF();
+ private boolean mShouldHideMenuAfterFling;
@Override
public void onDown(PipTouchState touchState) {
@@ -892,26 +892,22 @@
final float velocity = PointF.length(vel.x, vel.y);
if (touchState.isDragging()) {
- Runnable endAction = null;
if (mMenuState != MENU_STATE_NONE) {
// If the menu is still visible, then just poke the menu so that
// it will timeout after the user stops touching it
mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(),
true /* allowMenuTimeout */, willResizeMenu());
- } else {
- // If the menu is not visible, then we can still be showing the activity for the
- // dismiss overlay, so just finish it after the animation completes
- endAction = mMenuController::hideMenu;
}
+ mShouldHideMenuAfterFling = mMenuState == MENU_STATE_NONE;
mMotionHelper.flingToSnapTarget(vel.x, vel.y,
PipTouchHandler.this::updateDismissFraction /* updateAction */,
- endAction /* endAction */);
+ this::flingEndAction /* endAction */);
} else if (mTouchState.isDoubleTap()) {
// Expand to fullscreen if this is a double tap
// the PiP should be frozen until the transition ends
setTouchEnabled(false);
- mMotionHelper.expandPip();
+ mMotionHelper.expandPipToFullscreen();
} else if (mMenuState != MENU_STATE_FULL) {
if (!mTouchState.isWaitingForDoubleTap()) {
// User has stalled long enough for this not to be a drag or a double tap, just
@@ -927,6 +923,15 @@
}
return true;
}
+
+ private void flingEndAction() {
+ mTouchState.setAllowTouches(true);
+ if (mShouldHideMenuAfterFling) {
+ // If the menu is not visible, then we can still be showing the activity for the
+ // dismiss overlay, so just finish it after the animation completes
+ mMenuController.hideMenu();
+ }
+ }
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index f49732d..6282236 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -468,7 +468,7 @@
mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, mResizeAnimationDuration,
null);
} else {
- mPipTaskOrganizer.dismissPip(mResizeAnimationDuration);
+ mPipTaskOrganizer.exitPip(mResizeAnimationDuration);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index c8cf02a..10b04c0 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -302,13 +302,15 @@
}
private void showAutoSaverSuggestionNotification() {
+ final CharSequence message = mContext.getString(R.string.auto_saver_text);
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.HINTS)
.setSmallIcon(R.drawable.ic_power_saver)
.setWhen(0)
.setShowWhen(false)
.setContentTitle(mContext.getString(R.string.auto_saver_title))
- .setContentText(mContext.getString(R.string.auto_saver_text));
+ .setStyle(new Notification.BigTextStyle().bigText(message))
+ .setContentText(message);
nb.setContentIntent(pendingBroadcast(ACTION_ENABLE_AUTO_SAVER));
nb.setDeleteIntent(pendingBroadcast(ACTION_DISMISS_AUTO_SAVER_SUGGESTION));
nb.addAction(0,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index b157f4b..aaff9ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -152,15 +152,33 @@
}
@Override
+ public void fakeDragBy(float xOffset) {
+ try {
+ super.fakeDragBy(xOffset);
+ // Keep on drawing until the animation has finished.
+ postInvalidateOnAnimation();
+ } catch (NullPointerException e) {
+ Log.e(TAG, "FakeDragBy called before begin", e);
+ // If we were trying to fake drag, it means we just added a new tile to the last
+ // page, so animate there.
+ final int lastPageNumber = mPages.size() - 1;
+ post(() -> {
+ setCurrentItem(lastPageNumber, true);
+ if (mBounceAnimatorSet != null) {
+ mBounceAnimatorSet.start();
+ }
+ setOffscreenPageLimit(1);
+ });
+ }
+ }
+
+ @Override
public void computeScroll() {
if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
if (!isFakeDragging()) {
beginFakeDrag();
}
fakeDragBy(getScrollX() - mScroller.getCurrX());
- // Keep on drawing until the animation has finished.
- postInvalidateOnAnimation();
- return;
} else if (isFakeDragging()) {
endFakeDrag();
mBounceAnimatorSet.start();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 80e5071..5e3e438 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -21,7 +21,6 @@
import static com.android.systemui.util.Utils.useQsMediaPlayer;
import android.annotation.Nullable;
-import android.app.Notification;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -30,8 +29,6 @@
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
import android.media.MediaDescription;
import android.metrics.LogMaker;
import android.os.Bundle;
@@ -50,7 +47,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
@@ -60,7 +56,6 @@
import com.android.systemui.media.MediaControlPanel;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
@@ -70,7 +65,6 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSliderView;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
import com.android.systemui.tuner.TunerService;
@@ -545,6 +539,7 @@
public void setListening(boolean listening) {
if (mListening == listening) return;
+ mListening = listening;
if (mTileLayout != null) {
mQSLogger.logAllTilesChangeListening(listening, getDumpableTag(), mCachedSpecs);
mTileLayout.setListening(listening);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 54a928d..4008918 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -339,13 +339,18 @@
changeTileSpecs(tileSpecs-> !tileSpecs.contains(spec) && tileSpecs.add(spec));
}
+ private void saveTilesToSettings(List<String> tileSpecs) {
+ Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
+ TextUtils.join(",", tileSpecs), null /* tag */,
+ false /* default */, mCurrentUser, true /* overrideable by restore */);
+ }
+
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
- TILES_SETTING, ActivityManager.getCurrentUser());
+ TILES_SETTING, mCurrentUser);
final List<String> tileSpecs = loadTileSpecs(mContext, setting);
if (changeFunction.test(tileSpecs)) {
- Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
- TextUtils.join(",", tileSpecs), ActivityManager.getCurrentUser());
+ saveTilesToSettings(tileSpecs);
}
}
@@ -375,7 +380,7 @@
Intent intent = new Intent().setComponent(component);
TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(),
mContext, mServices, new Tile(), intent,
- new UserHandle(ActivityManager.getCurrentUser()),
+ new UserHandle(mCurrentUser),
mBroadcastDispatcher);
lifecycleManager.onStopListening();
lifecycleManager.onTileRemoved();
@@ -384,8 +389,7 @@
}
}
if (DEBUG) Log.d(TAG, "saveCurrentTiles " + newTiles);
- Secure.putStringForUser(getContext().getContentResolver(), QSTileHost.TILES_SETTING,
- TextUtils.join(",", newTiles), ActivityManager.getCurrentUser());
+ saveTilesToSettings(newTiles);
}
public QSTile createTile(String tileSpec) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 02a7aca..db33c79 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -592,13 +592,11 @@
removeDivider();
addDivider(configuration);
- if (mView != null) {
- if (mMinimized) {
- mView.setMinimizedDockStack(true, mHomeStackResizable);
- updateTouchable();
- }
- mView.setHidden(isDividerHidden);
+ if (mMinimized) {
+ mView.setMinimizedDockStack(true, mHomeStackResizable);
+ updateTouchable();
}
+ mView.setHidden(isDividerHidden);
}
void onTaskVanished() {
@@ -610,7 +608,7 @@
mContext.getDisplayId()).getResources().getConfiguration()));
}
- void updateVisibility(final boolean visible) {
+ private void updateVisibility(final boolean visible) {
if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
if (mVisible != visible) {
mVisible = visible;
@@ -639,6 +637,7 @@
void onSplitDismissed() {
mMinimized = false;
updateVisibility(false /* visible */);
+ removeDivider();
}
/** Switch to minimized state if appropriate */
@@ -788,6 +787,8 @@
}
void startEnterSplit() {
+ update(mDisplayController.getDisplayContext(
+ mContext.getDisplayId()).getResources().getConfiguration());
// Set resizable directly here because applyEnterSplit already resizes home stack.
mHomeStackResizable = WindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index e7f2618..765f85a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -195,13 +195,15 @@
boolean wasChildInGroup = ent.isChildInGroup();
if (isChildInGroup && !wasChildInGroup) {
isChildInGroup = wasChildInGroup;
- mVisualStabilityManager.addGroupChangesAllowedCallback(mEntryManager);
+ mVisualStabilityManager.addGroupChangesAllowedCallback(mEntryManager,
+ false /* persistent */);
} else if (!isChildInGroup && wasChildInGroup) {
// We allow grouping changes if the group was collapsed
if (mGroupManager.isLogicalGroupExpanded(ent.getSbn())) {
isChildInGroup = wasChildInGroup;
parent = ent.getRow().getNotificationParent().getEntry();
- mVisualStabilityManager.addGroupChangesAllowedCallback(mEntryManager);
+ mVisualStabilityManager.addGroupChangesAllowedCallback(mEntryManager,
+ false /* persistent */);
}
}
}
@@ -286,7 +288,8 @@
if (mVisualStabilityManager.canReorderNotification(targetChild)) {
mListContainer.changeViewPosition(targetChild, i);
} else {
- mVisualStabilityManager.addReorderingAllowedCallback(mEntryManager);
+ mVisualStabilityManager.addReorderingAllowedCallback(mEntryManager,
+ false /* persistent */);
}
}
j++;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 02c98ae..88f148b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -121,14 +121,14 @@
}
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
- return canHandleMotionEvent() && startExpansion(event)
+ return maybeStartExpansion(event)
}
- private fun canHandleMotionEvent(): Boolean {
- return !wakeUpCoordinator.canShowPulsingHuns || qsExpanded || bouncerShowing
- }
-
- private fun startExpansion(event: MotionEvent): Boolean {
+ private fun maybeStartExpansion(event: MotionEvent): Boolean {
+ if (!wakeUpCoordinator.canShowPulsingHuns || qsExpanded ||
+ bouncerShowing) {
+ return false
+ }
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain()
}
@@ -177,14 +177,9 @@
}
override fun onTouchEvent(event: MotionEvent): Boolean {
- if (!canHandleMotionEvent()) {
- return false
+ if (!isExpanding) {
+ return maybeStartExpansion(event)
}
-
- if (!isExpanding || event.actionMasked == MotionEvent.ACTION_DOWN) {
- return startExpansion(event)
- }
-
velocityTracker!!.addMovement(event)
val y = event.y
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index e2b01ff..d7b391f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -22,6 +22,7 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_FOREGROUND_SERVICE
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_MEDIA_CONTROLS
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
@@ -52,12 +53,14 @@
fun getNotificationBuckets(): IntArray {
return when {
isFilteringEnabled() && isMediaControlsEnabled() ->
- intArrayOf(BUCKET_HEADS_UP, BUCKET_MEDIA_CONTROLS, BUCKET_PEOPLE, BUCKET_ALERTING,
- BUCKET_SILENT)
+ intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS,
+ BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT)
!isFilteringEnabled() && isMediaControlsEnabled() ->
- intArrayOf(BUCKET_HEADS_UP, BUCKET_MEDIA_CONTROLS, BUCKET_ALERTING, BUCKET_SILENT)
+ intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS,
+ BUCKET_ALERTING, BUCKET_SILENT)
isFilteringEnabled() && !isMediaControlsEnabled() ->
- intArrayOf(BUCKET_HEADS_UP, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT)
+ intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_PEOPLE,
+ BUCKET_ALERTING, BUCKET_SILENT)
NotificationUtils.useNewInterruptionModel(context) ->
intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
else ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 7ac5995..8341c02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -43,7 +43,9 @@
private static final long TEMPORARY_REORDERING_ALLOWED_DURATION = 1000;
private final ArrayList<Callback> mReorderingAllowedCallbacks = new ArrayList<>();
+ private final ArraySet<Callback> mPersistentReorderingCallbacks = new ArraySet<>();
private final ArrayList<Callback> mGroupChangesAllowedCallbacks = new ArrayList<>();
+ private final ArraySet<Callback> mPersistentGroupCallbacks = new ArraySet<>();
private final Handler mHandler;
private boolean mPanelExpanded;
@@ -85,8 +87,15 @@
/**
* Add a callback to invoke when reordering is allowed again.
+ *
+ * @param callback the callback to add
+ * @param persistent {@code true} if this callback should this callback be persisted, otherwise
+ * it will be removed after a single invocation
*/
- public void addReorderingAllowedCallback(Callback callback) {
+ public void addReorderingAllowedCallback(Callback callback, boolean persistent) {
+ if (persistent) {
+ mPersistentReorderingCallbacks.add(callback);
+ }
if (mReorderingAllowedCallbacks.contains(callback)) {
return;
}
@@ -95,8 +104,15 @@
/**
* Add a callback to invoke when group changes are allowed again.
+ *
+ * @param callback the callback to add
+ * @param persistent {@code true} if this callback should this callback be persisted, otherwise
+ * it will be removed after a single invocation
*/
- public void addGroupChangesAllowedCallback(Callback callback) {
+ public void addGroupChangesAllowedCallback(Callback callback, boolean persistent) {
+ if (persistent) {
+ mPersistentGroupCallbacks.add(callback);
+ }
if (mGroupChangesAllowedCallbacks.contains(callback)) {
return;
}
@@ -136,21 +152,26 @@
boolean changedToTrue = reorderingAllowed && !mReorderingAllowed;
mReorderingAllowed = reorderingAllowed;
if (changedToTrue) {
- notifyChangeAllowed(mReorderingAllowedCallbacks);
+ notifyChangeAllowed(mReorderingAllowedCallbacks, mPersistentReorderingCallbacks);
}
boolean groupChangesAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
changedToTrue = groupChangesAllowed && !mGroupChangedAllowed;
mGroupChangedAllowed = groupChangesAllowed;
if (changedToTrue) {
- notifyChangeAllowed(mGroupChangesAllowedCallbacks);
+ notifyChangeAllowed(mGroupChangesAllowedCallbacks, mPersistentGroupCallbacks);
}
}
- private void notifyChangeAllowed(ArrayList<Callback> callbacks) {
+ private void notifyChangeAllowed(ArrayList<Callback> callbacks,
+ ArraySet<Callback> persistentCallbacks) {
for (int i = 0; i < callbacks.size(); i++) {
- callbacks.get(i).onChangeAllowed();
+ Callback callback = callbacks.get(i);
+ callback.onChangeAllowed();
+ if (!persistentCallbacks.contains(callback)) {
+ callbacks.remove(callback);
+ i--;
+ }
}
- callbacks.clear();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index c78370e..9ac4229 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_FOREGROUND_SERVICE
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
@@ -162,6 +163,8 @@
val isMedia = isImportantMedia(entry)
val isSystemMax = entry.isSystemMax()
return when {
+ entry.sbn.notification.isForegroundService && entry.sbn.notification.isColorized ->
+ BUCKET_FOREGROUND_SERVICE
usePeopleFiltering && entry.getPeopleNotificationType() != TYPE_NON_PERSON ->
BUCKET_PEOPLE
isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 92426e5..57be1a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -19,6 +19,8 @@
import static android.service.notification.NotificationStats.DISMISSAL_OTHER;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import android.annotation.NonNull;
+
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -121,7 +123,7 @@
private final BubbleController.NotifCallback mNotifCallback =
new BubbleController.NotifCallback() {
@Override
- public void removeNotification(NotificationEntry entry, int reason) {
+ public void removeNotification(@NonNull final NotificationEntry entry, int reason) {
if (isInterceptingDismissal(entry)) {
mInterceptedDismissalEntries.remove(entry.getKey());
mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
@@ -141,7 +143,7 @@
}
@Override
- public void maybeCancelSummary(NotificationEntry entry) {
+ public void maybeCancelSummary(@NonNull final NotificationEntry entry) {
// no-op
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
index cc5de65..66c07bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
@@ -135,10 +135,13 @@
}
private void bindActions() {
+ final OnClickListener settingsOnClickListener = getSettingsOnClickListener();
final View settingsButton = findViewById(R.id.info);
- settingsButton.setOnClickListener(getSettingsOnClickListener());
+ settingsButton.setOnClickListener(settingsOnClickListener);
settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
+ findViewById(R.id.settings_link).setOnClickListener(settingsOnClickListener);
+
TextView msg = findViewById(R.id.non_configurable_text);
msg.setText(getResources().getString(R.string.no_shortcut, mAppName));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index c9b1318..99691b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -463,7 +463,8 @@
mAttachedChildren.add(i, desiredChild);
result = true;
} else {
- visualStabilityManager.addReorderingAllowedCallback(callback);
+ visualStabilityManager.addReorderingAllowedCallback(callback,
+ false /* persistent */);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index dcf30ca..b5ba3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -29,12 +29,10 @@
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.media.KeyguardMediaController;
-import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
@@ -329,8 +327,6 @@
boolean peopleNotifsPresent = false;
int currentMediaControlsIdx = -1;
- // Currently, just putting media controls in the front and incrementing the position based
- // on the number of heads-up notifs.
int mediaControlsTarget = usingMediaControls ? 0 : -1;
int currentIncomingHeaderIdx = -1;
int incomingHeaderTarget = -1;
@@ -408,6 +404,11 @@
mediaControlsTarget++;
}
break;
+ case BUCKET_FOREGROUND_SERVICE:
+ if (mediaControlsTarget != -1) {
+ mediaControlsTarget++;
+ }
+ break;
case BUCKET_PEOPLE:
mLogger.logPosition(i, "Conversation");
peopleNotifsPresent = true;
@@ -488,7 +489,8 @@
adjustHeaderVisibilityAndPosition(
peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx);
adjustViewPosition(mediaControlsTarget, mMediaControlsView, currentMediaControlsIdx);
- adjustViewPosition(incomingHeaderTarget, mIncomingHeader, currentIncomingHeaderIdx);
+ adjustHeaderVisibilityAndPosition(incomingHeaderTarget, mIncomingHeader,
+ currentIncomingHeaderIdx);
mLogger.logStr("Final order:");
@@ -508,45 +510,29 @@
private void adjustHeaderVisibilityAndPosition(
int targetPosition, StackScrollerDecorView header, int currentPosition) {
- if (targetPosition == -1) {
- if (currentPosition != -1) {
- mParent.removeView(header);
- }
- } else {
- if (currentPosition == -1) {
- // If the header is animating away, it will still have a parent, so detach it first
- // TODO: We should really cancel the active animations here. This will happen
- // automatically when the view's intro animation starts, but it's a fragile link.
- if (header.getTransientContainer() != null) {
- header.getTransientContainer().removeTransientView(header);
- header.setTransientContainer(null);
- }
- header.setContentVisible(true);
- mParent.addView(header, targetPosition);
- } else {
- mParent.changeViewPosition(header, targetPosition);
- }
+ adjustViewPosition(targetPosition, header, currentPosition);
+ if (targetPosition != -1 && currentPosition == -1) {
+ header.setContentVisible(true);
}
}
- private void adjustViewPosition(int targetPosition, ExpandableView header,
- int currentPosition) {
+ private void adjustViewPosition(int targetPosition, ExpandableView view, int currentPosition) {
if (targetPosition == -1) {
if (currentPosition != -1) {
- mParent.removeView(header);
+ mParent.removeView(view);
}
} else {
if (currentPosition == -1) {
// If the header is animating away, it will still have a parent, so detach it first
// TODO: We should really cancel the active animations here. This will happen
// automatically when the view's intro animation starts, but it's a fragile link.
- if (header.getTransientContainer() != null) {
- header.getTransientContainer().removeTransientView(header);
- header.setTransientContainer(null);
+ if (view.getTransientContainer() != null) {
+ view.getTransientContainer().removeTransientView(view);
+ view.setTransientContainer(null);
}
- mParent.addView(header, targetPosition);
+ mParent.addView(view, targetPosition);
} else {
- mParent.changeViewPosition(header, targetPosition);
+ mParent.changeViewPosition(view, targetPosition);
}
}
}
@@ -641,6 +627,11 @@
}
@VisibleForTesting
+ ExpandableView getIncomingHeaderView() {
+ return mIncomingHeader;
+ }
+
+ @VisibleForTesting
void setPeopleHubVisible(boolean visible) {
mPeopleHubVisible = visible;
}
@@ -685,6 +676,7 @@
@Retention(SOURCE)
@IntDef(prefix = { "BUCKET_" }, value = {
BUCKET_HEADS_UP,
+ BUCKET_FOREGROUND_SERVICE,
BUCKET_MEDIA_CONTROLS,
BUCKET_PEOPLE,
BUCKET_ALERTING,
@@ -692,8 +684,9 @@
})
public @interface PriorityBucket {}
public static final int BUCKET_HEADS_UP = 0;
- public static final int BUCKET_MEDIA_CONTROLS = 1;
- public static final int BUCKET_PEOPLE = 2;
- public static final int BUCKET_ALERTING = 3;
- public static final int BUCKET_SILENT = 4;
+ public static final int BUCKET_FOREGROUND_SERVICE = 1;
+ public static final int BUCKET_MEDIA_CONTROLS = 2;
+ public static final int BUCKET_PEOPLE = 3;
+ public static final int BUCKET_ALERTING = 4;
+ public static final int BUCKET_SILENT = 5;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 6b0df95..3dcf7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -439,7 +439,8 @@
// time out anyway
&& !entry.showingPulsing()) {
mEntriesToRemoveWhenReorderingAllowed.add(entry);
- mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManagerPhone.this);
+ mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManagerPhone.this,
+ false /* persistent */);
} else if (mTrackingHeadsUp) {
mEntriesToRemoveAfterExpand.add(entry);
} else if (mIsAutoHeadsUp && mStatusBarState == StatusBarState.KEYGUARD) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 80785db..d0a872e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
@@ -454,7 +455,7 @@
* If there is a {@link NotificationGroup} associated with the provided entry, this method
* will update the suppression of that group.
*/
- public void updateSuppression(NotificationEntry entry) {
+ public void updateSuppression(@NonNull final NotificationEntry entry) {
NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
if (group != null) {
updateSuppression(group);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 2e4a929d..926c1bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -321,8 +321,7 @@
|| state.mPanelVisible || state.mKeyguardFadingAway || state.mBouncerShowing
|| state.mHeadsUpShowing
|| state.mScrimsVisibility != ScrimController.TRANSPARENT)
- || state.mBackgroundBlurRadius > 0
- || state.mLaunchingActivity;
+ || state.mBackgroundBlurRadius > 0;
}
private void applyFitsSystemWindows(State state) {
@@ -486,11 +485,6 @@
apply(mCurrentState);
}
- void setLaunchingActivity(boolean launching) {
- mCurrentState.mLaunchingActivity = launching;
- apply(mCurrentState);
- }
-
public void setScrimsVisibility(int scrimsVisibility) {
mCurrentState.mScrimsVisibility = scrimsVisibility;
apply(mCurrentState);
@@ -651,7 +645,6 @@
boolean mForceCollapsed;
boolean mForceDozeBrightness;
boolean mForceUserActivity;
- boolean mLaunchingActivity;
boolean mBackdropShowing;
boolean mWallpaperSupportsAmbientMode;
boolean mNotTouchable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 0d25898..596a607 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -93,7 +93,6 @@
private PhoneStatusBarView mStatusBarView;
private PhoneStatusBarTransitions mBarTransitions;
private StatusBar mService;
- private NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
private boolean mDoubleTapEnabled;
private boolean mSingleTapEnabled;
@@ -431,14 +430,10 @@
public void setExpandAnimationPending(boolean pending) {
mExpandAnimationPending = pending;
- mNotificationShadeWindowController
- .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
}
public void setExpandAnimationRunning(boolean running) {
mExpandAnimationRunning = running;
- mNotificationShadeWindowController
- .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
}
public void cancelExpandHelper() {
@@ -461,9 +456,8 @@
}
}
- public void setService(StatusBar statusBar, NotificationShadeWindowController controller) {
+ public void setService(StatusBar statusBar) {
mService = statusBar;
- mNotificationShadeWindowController = controller;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index dd54a3d..bbf83bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1001,7 +1001,7 @@
updateTheme();
inflateStatusBarWindow();
- mNotificationShadeWindowViewController.setService(this, mNotificationShadeWindowController);
+ mNotificationShadeWindowViewController.setService(this);
mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener());
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 442c7ea..b6e72226 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -369,16 +369,24 @@
R.string.ext_media_new_notification_message, disk.getDescription());
final PendingIntent initIntent = buildInitPendingIntent(vol);
- return buildNotificationBuilder(vol, title, text)
- .addAction(new Action(R.drawable.ic_settings_24dp,
- mContext.getString(R.string.ext_media_init_action), initIntent))
- .addAction(new Action(R.drawable.ic_eject_24dp,
- mContext.getString(R.string.ext_media_unmount_action),
- buildUnmountPendingIntent(vol)))
- .setContentIntent(initIntent)
- .setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()))
- .build();
+ final PendingIntent unmountIntent = buildUnmountPendingIntent(vol);
+ if (isAutomotive()) {
+ return buildNotificationBuilder(vol, title, text)
+ .setContentIntent(unmountIntent)
+ .setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()))
+ .build();
+ } else {
+ return buildNotificationBuilder(vol, title, text)
+ .addAction(new Action(R.drawable.ic_settings_24dp,
+ mContext.getString(R.string.ext_media_init_action), initIntent))
+ .addAction(new Action(R.drawable.ic_eject_24dp,
+ mContext.getString(R.string.ext_media_unmount_action),
+ unmountIntent))
+ .setContentIntent(initIntent)
+ .setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()))
+ .build();
+ }
} else {
final CharSequence title = disk.getDescription();
final CharSequence text = mContext.getString(
@@ -427,9 +435,15 @@
R.string.ext_media_unmountable_notification_title, disk.getDescription());
final CharSequence text = mContext.getString(
R.string.ext_media_unmountable_notification_message, disk.getDescription());
+ PendingIntent action;
+ if (isAutomotive()) {
+ action = buildUnmountPendingIntent(vol);
+ } else {
+ action = buildInitPendingIntent(vol);
+ }
return buildNotificationBuilder(vol, title, text)
- .setContentIntent(buildInitPendingIntent(vol))
+ .setContentIntent(action)
.setCategory(Notification.CATEGORY_ERROR)
.build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 367d4d2..2973e0a 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -23,15 +23,12 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.ServiceManager;
-import android.util.EventLog;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.CheckBox;
-import android.widget.Toast;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
@@ -79,25 +76,6 @@
window.setCloseOnTouchOutside(false);
setupAlert();
-
- // adding touch listener on affirmative button - checks if window is obscured
- // if obscured, do not let user give permissions (could be tapjacking involved)
- final View.OnTouchListener filterTouchListener = (View v, MotionEvent event) -> {
- // Filter obscured touches by consuming them.
- if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0)
- || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) {
- if (event.getAction() == MotionEvent.ACTION_UP) {
- EventLog.writeEvent(0x534e4554, "62187985"); // safety net logging
- Toast.makeText(v.getContext(),
- R.string.touch_filtered_warning,
- Toast.LENGTH_SHORT).show();
- }
- return true;
- }
- return false;
- };
- mAlert.getButton(BUTTON_POSITIVE).setOnTouchListener(filterTouchListener);
-
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
index 93f45c5..21f67ae 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
@@ -125,7 +125,7 @@
*/
public void removeView(View view) {
SurfaceControlViewHost root = mViewRoots.remove(view);
- root.die();
+ root.release();
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index 870010a..aa4122f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -134,6 +134,7 @@
mDependency.injectMockDependency(WakefulnessLifecycle.class);
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
new Handler(mTestableLooper.getLooper()));
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
doAnswer(this::checkMainThread).when(mKeyguardUpdateMonitor)
@@ -150,6 +151,7 @@
// This should not start listening on any of the real dependencies but will test that
// callbacks in mKeyguardUpdateMonitor are done in the mTestableLooper thread
mCarrierTextController.setListening(mCarrierTextCallback);
+ mTestableLooper.processAllMessages();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 96e868d..52923ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -317,7 +317,7 @@
verify(mNotificationEntryManager).updateNotifications(any());
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
@@ -329,7 +329,7 @@
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey());
assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b));
@@ -350,9 +350,10 @@
mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */
false, /* showInShade */ true);
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
- mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleController.removeBubble(
+ mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getEntry().getSbn()), anyInt());
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
@@ -365,7 +366,7 @@
assertTrue(mBubbleController.hasBubbles());
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_USER_CHANGED);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_CHANGED);
verify(mNotificationEntryManager, never()).performRemoveNotification(
eq(mRow.getEntry().getSbn()), anyInt());
assertFalse(mBubbleController.hasBubbles());
@@ -563,7 +564,8 @@
// Dismiss currently expanded
mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey())
+ .getEntry().getKey(),
BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
@@ -574,7 +576,8 @@
// Dismiss that one
mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey())
+ .getEntry().getKey(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
@@ -700,7 +703,7 @@
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_AGED);
+ mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@@ -708,7 +711,7 @@
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@@ -808,7 +811,7 @@
// Dismiss the bubble into overflow.
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
@@ -829,7 +832,7 @@
mRow.getEntry()));
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_NO_LONGER_BUBBLE);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_NO_LONGER_BUBBLE);
assertFalse(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
@@ -851,12 +854,12 @@
mBubbleData.setMaxOverflowBubbles(1);
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
assertEquals(mBubbleData.getBubbles().size(), 2);
assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
mBubbleController.removeBubble(
- mRow2.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mRow2.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
// Overflow max of 1 is reached; mRow is oldest, so it gets removed
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
mRow.getEntry().getSbn(), REASON_CANCEL);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 66f119a..882504b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -20,11 +20,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -180,7 +177,8 @@
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
// Verify
verifyUpdateReceived();
@@ -297,12 +295,14 @@
mBubbleData.setListener(mListener);
mBubbleData.setMaxOverflowBubbles(1);
- mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
// Overflow max of 1 is reached; A1 is oldest, so it gets removed
- mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
}
@@ -449,7 +449,8 @@
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA1);
}
@@ -469,7 +470,8 @@
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(
+ mEntryB1.getKey(), BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOrderNotChanged();
}
@@ -489,7 +491,8 @@
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA1.getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA2);
}
@@ -510,12 +513,14 @@
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA1.getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
// Test
- mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_GROUP_CANCELLED);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA2.getKey(), BubbleController.DISMISS_GROUP_CANCELLED);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of());
}
@@ -534,7 +539,8 @@
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA2.getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB2);
}
@@ -625,7 +631,8 @@
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
// Verify the selection was cleared.
verifyUpdateReceived();
@@ -779,7 +786,8 @@
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(
+ mEntryB2.getKey(), BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB1, mBubbleA2, mBubbleA1);
}
@@ -804,11 +812,13 @@
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleA1);
- mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB1);
}
@@ -923,7 +933,8 @@
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(
+ mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertExpandedChangedTo(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 73b8760..c16801c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -285,7 +285,8 @@
assertTrue(mBubbleController.hasBubbles());
verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
- mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.removeBubble(
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
}
@@ -302,7 +303,8 @@
mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
- mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.removeBubble(
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
// We don't remove the notification since the bubble is still in overflow.
@@ -322,7 +324,8 @@
mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
- mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleController.removeBubble(
+ mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
// Since the notif is dismissed and not in overflow, once the bubble is removed,
@@ -502,7 +505,8 @@
// Dismiss currently expanded
mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
+ mBubbleData.getBubbleInStackWithKey(
+ stackView.getExpandedBubble().getKey()).getEntry().getKey(),
BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
@@ -513,7 +517,8 @@
// Dismiss that one
mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
+ mBubbleData.getBubbleInStackWithKey(
+ stackView.getExpandedBubble().getKey()).getEntry().getKey(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
@@ -613,7 +618,7 @@
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_AGED);
+ mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@@ -621,7 +626,7 @@
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@@ -686,7 +691,7 @@
// Dismiss the bubble
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
// Dismiss the notification
@@ -707,7 +712,7 @@
// Dismiss the bubble
mBubbleController.removeBubble(
- mRow.getEntry(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleController.hasBubbles());
// Dismiss the notification
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 64acdcc..ee7733a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -156,13 +156,20 @@
}
@Test
- public void testShouldLogVisibility() {
+ public void testShouldLogShow() {
mGlobalActionsDialog.onShow(null);
mTestableLooper.processAllMessages();
verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_POWER_MENU_OPEN);
}
@Test
+ public void testShouldLogDismiss() {
+ mGlobalActionsDialog.onDismiss(mGlobalActionsDialog.mDialog);
+ mTestableLooper.processAllMessages();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_POWER_MENU_CLOSE);
+ }
+
+ @Test
public void testShouldLogBugreportPress() throws InterruptedException {
GlobalActionsDialog.BugReportAction bugReportAction =
mGlobalActionsDialog.makeBugReportActionForTesting();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
index d407b8a..58ee79e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
@@ -16,19 +16,15 @@
package com.android.systemui.media
-import android.graphics.Color
-import android.content.res.ColorStateList
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.widget.SeekBar
import android.widget.TextView
import androidx.test.filters.SmallTest
-
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
-
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -65,7 +61,7 @@
fun seekBarGone() {
// WHEN seek bar is disabled
val isEnabled = false
- val data = SeekBarViewModel.Progress(isEnabled, false, null, null, null)
+ val data = SeekBarViewModel.Progress(isEnabled, false, null, null)
observer.onChanged(data)
// THEN seek bar shows just a line with no text
assertThat(seekBarView.isEnabled()).isFalse()
@@ -78,7 +74,7 @@
fun seekBarVisible() {
// WHEN seek bar is enabled
val isEnabled = true
- val data = SeekBarViewModel.Progress(isEnabled, true, 3000, 12000, -1)
+ val data = SeekBarViewModel.Progress(isEnabled, true, 3000, 12000)
observer.onChanged(data)
// THEN seek bar is visible
assertThat(seekBarView.getVisibility()).isEqualTo(View.VISIBLE)
@@ -89,7 +85,7 @@
@Test
fun seekBarProgress() {
// WHEN seek bar progress is about half
- val data = SeekBarViewModel.Progress(true, true, 3000, 120000, -1)
+ val data = SeekBarViewModel.Progress(true, true, 3000, 120000)
observer.onChanged(data)
// THEN seek bar is visible
assertThat(seekBarView.progress).isEqualTo(100)
@@ -102,7 +98,7 @@
fun seekBarDisabledWhenSeekNotAvailable() {
// WHEN seek is not available
val isSeekAvailable = false
- val data = SeekBarViewModel.Progress(true, isSeekAvailable, 3000, 120000, -1)
+ val data = SeekBarViewModel.Progress(true, isSeekAvailable, 3000, 120000)
observer.onChanged(data)
// THEN seek bar is not enabled
assertThat(seekBarView.isEnabled()).isFalse()
@@ -112,20 +108,9 @@
fun seekBarEnabledWhenSeekNotAvailable() {
// WHEN seek is available
val isSeekAvailable = true
- val data = SeekBarViewModel.Progress(true, isSeekAvailable, 3000, 120000, -1)
+ val data = SeekBarViewModel.Progress(true, isSeekAvailable, 3000, 120000)
observer.onChanged(data)
// THEN seek bar is not enabled
assertThat(seekBarView.isEnabled()).isTrue()
}
-
- @Test
- fun seekBarColor() {
- // WHEN data included color
- val data = SeekBarViewModel.Progress(true, true, 3000, 120000, Color.RED)
- observer.onChanged(data)
- // THEN seek bar is colored
- val red = ColorStateList.valueOf(Color.RED)
- assertThat(elapsedTimeView.getTextColors()).isEqualTo(red)
- assertThat(totalTimeView.getTextColors()).isEqualTo(red)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index cde575d..e5ced0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -80,12 +80,6 @@
}
@Test
- fun updateColor() {
- viewModel.updateController(mockController, Color.RED)
- assertThat(viewModel.progress.value!!.color).isEqualTo(Color.RED)
- }
-
- @Test
fun updateDurationWithPlayback() {
// GIVEN that the duration is contained within the metadata
val duration = 12000L
@@ -101,7 +95,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN the duration is extracted
assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
assertThat(viewModel.progress.value!!.enabled).isTrue()
@@ -117,7 +111,7 @@
}
whenever(mockController.getMetadata()).thenReturn(metadata)
// WHEN the controller is updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN the duration is extracted
assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
assertThat(viewModel.progress.value!!.enabled).isFalse()
@@ -139,7 +133,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN the seek bar is disabled
assertThat(viewModel.progress.value!!.enabled).isFalse()
}
@@ -160,7 +154,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN the seek bar is disabled
assertThat(viewModel.progress.value!!.enabled).isFalse()
}
@@ -175,7 +169,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN elapsed time is captured
assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(200.toInt())
}
@@ -189,7 +183,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN seek is available
assertThat(viewModel.progress.value!!.seekAvailable).isTrue()
}
@@ -203,7 +197,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN seek is not available
assertThat(viewModel.progress.value!!.seekAvailable).isFalse()
}
@@ -211,7 +205,7 @@
@Test
fun handleSeek() {
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// WHEN user input is dispatched
val pos = 42L
viewModel.onSeek(pos)
@@ -223,7 +217,7 @@
@Test
fun handleProgressChangedUser() {
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// WHEN user starts dragging the seek bar
val pos = 42
viewModel.seekBarListener.onProgressChanged(SeekBar(context), pos, true)
@@ -235,7 +229,7 @@
@Test
fun handleProgressChangedOther() {
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// WHEN user starts dragging the seek bar
val pos = 42
viewModel.seekBarListener.onProgressChanged(SeekBar(context), pos, false)
@@ -247,7 +241,7 @@
@Test
fun handleStartTrackingTouch() {
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// WHEN user starts dragging the seek bar
val pos = 42
val bar = SeekBar(context).apply {
@@ -262,7 +256,7 @@
@Test
fun handleStopTrackingTouch() {
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// WHEN user ends drag
val pos = 42
val bar = SeekBar(context).apply {
@@ -283,7 +277,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN a task is queued
assertThat(fakeExecutor.numPending()).isEqualTo(1)
}
@@ -297,7 +291,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN an update task is not queued
assertThat(fakeExecutor.numPending()).isEqualTo(0)
}
@@ -317,7 +311,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN an update task is queued
assertThat(fakeExecutor.numPending()).isEqualTo(1)
}
@@ -337,7 +331,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// THEN an update task is not queued
assertThat(fakeExecutor.numPending()).isEqualTo(0)
}
@@ -350,7 +344,7 @@
build()
}
whenever(mockController.getPlaybackState()).thenReturn(state)
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// WHEN the next task runs
with(fakeExecutor) {
advanceClockToNext()
@@ -369,7 +363,7 @@
build()
}
whenever(mockController.getPlaybackState()).thenReturn(state)
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// WHEN the task runs
with(fakeExecutor) {
advanceClockToNext()
@@ -393,7 +387,7 @@
build()
}
whenever(mockController.getPlaybackState()).thenReturn(state)
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// WHEN start listening
viewModel.listening = true
// THEN an update task is queued
@@ -415,7 +409,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
// AND the controller has been updated
- viewModel.updateController(mockController, Color.RED)
+ viewModel.updateController(mockController)
// WHEN the controller is cleared on the event when the session is destroyed
viewModel.clearController()
with(fakeExecutor) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
index 7211254..601fad6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
@@ -65,9 +65,6 @@
private IActivityManager mActivityManager;
@Mock
- private IActivityTaskManager mIActivityTaskManager;
-
- @Mock
private PipMenuActivityController mPipMenuActivityController;
@Mock
@@ -101,7 +98,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
- mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mIActivityTaskManager,
+ mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager,
mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy,
mPipSnapAlgorithm);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 6c543c7..05b31c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -161,6 +161,9 @@
mQsPanel.setListening(true);
verify(mQSLogger).logAllTilesChangeListening(true, mQsPanel.getDumpableTag(), "dnd");
+
+ mQsPanel.setListening(false);
+ verify(mQSLogger).logAllTilesChangeListening(false, mQsPanel.getDumpableTag(), "dnd");
}
/* @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
index ca7a5db..8948fd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
@@ -74,7 +74,7 @@
DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "true", false)
assertTrue("People filtering should be enabled", manager!!.isFilteringEnabled())
- assertTrue("Expecting 4 buckets when people filtering is enabled",
- manager!!.getNumberOfBuckets() == 4)
+ assertTrue("Expecting 5 buckets when people filtering is enabled",
+ manager!!.getNumberOfBuckets() == 5)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index 3d06c57..ea1b498 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -108,7 +109,7 @@
public void testCallBackCalledScreenOn() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
mVisualStabilityManager.setScreenOn(false);
verify(mCallback).onChangeAllowed();
}
@@ -117,7 +118,7 @@
public void testCallBackCalledPanelExpanded() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
mVisualStabilityManager.setPanelExpanded(false);
verify(mCallback).onChangeAllowed();
}
@@ -126,7 +127,7 @@
public void testCallBackExactlyOnce() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
mVisualStabilityManager.setScreenOn(false);
mVisualStabilityManager.setScreenOn(true);
mVisualStabilityManager.setScreenOn(false);
@@ -134,6 +135,17 @@
}
@Test
+ public void testCallBackCalledContinuouslyWhenRequested() {
+ mVisualStabilityManager.setPanelExpanded(true);
+ mVisualStabilityManager.setScreenOn(true);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback, true /* persistent */);
+ mVisualStabilityManager.setScreenOn(false);
+ mVisualStabilityManager.setScreenOn(true);
+ mVisualStabilityManager.setScreenOn(false);
+ verify(mCallback, times(2)).onChangeAllowed();
+ }
+
+ @Test
public void testAddedCanReorder() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
@@ -188,7 +200,7 @@
@Test
public void testCallBackCalled_Pulsing() {
mVisualStabilityManager.setPulsing(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
mVisualStabilityManager.setPulsing(false);
verify(mCallback).onChangeAllowed();
}
@@ -198,7 +210,7 @@
// GIVEN having the panel open (which would block reordering)
mVisualStabilityManager.setScreenOn(true);
mVisualStabilityManager.setPanelExpanded(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
// WHEN we temprarily allow reordering
mVisualStabilityManager.temporarilyAllowReordering();
@@ -212,7 +224,7 @@
public void testTemporarilyAllowReorderingDoesntOverridePulsing() {
// GIVEN we are in a pulsing state
mVisualStabilityManager.setPulsing(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
// WHEN we temprarily allow reordering
mVisualStabilityManager.temporarilyAllowReordering();
@@ -227,7 +239,7 @@
// GIVEN having the panel open (which would block reordering)
mVisualStabilityManager.setScreenOn(true);
mVisualStabilityManager.setPanelExpanded(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
// WHEN we temprarily allow reordering and then wait until the window expires
mVisualStabilityManager.temporarilyAllowReordering();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index 1bfebfb..f21b1a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -356,6 +356,30 @@
}
@Test
+ public void testBindNotification_SetsOnClickListenerForSettings_mainText() {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mChannelEditorDialogController,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ (View v, NotificationChannel c, int appUid) -> {
+ assertEquals(mNotificationChannel, c);
+ latch.countDown();
+ },
+ true,
+ false);
+
+ final View settingsButton = mInfo.findViewById(R.id.settings_link);
+ settingsButton.performClick();
+ // Verify that listener was triggered.
+ assertEquals(0, latch.getCount());
+ }
+
+ @Test
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() {
mInfo.bindNotification(
mMockPackageManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 0b86a78..546bce8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -19,15 +19,19 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_FOREGROUND_SERVICE;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -62,6 +66,9 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -84,10 +91,23 @@
@Before
public void setUp() {
- when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(2);
- when(mNotificationRowComponent.getActivatableNotificationViewController()).thenReturn(
- mActivatableNotificationViewController
- );
+ when(mSectionsFeatureManager.getNumberOfBuckets()).thenAnswer(
+ invocation -> {
+ int count = 2;
+ if (mSectionsFeatureManager.isFilteringEnabled()) {
+ count = 5;
+ }
+ if (mSectionsFeatureManager.isMediaControlsEnabled()) {
+ if (!mSectionsFeatureManager.isFilteringEnabled()) {
+ count = 5;
+ } else {
+ count += 1;
+ }
+ }
+ return count;
+ });
+ when(mNotificationRowComponent.getActivatableNotificationViewController())
+ .thenReturn(mActivatableNotificationViewController);
mSectionsManager =
new NotificationSectionsManager(
mActivityStarterDelegate,
@@ -104,6 +124,7 @@
mSectionsManager.initialize(mNssl, LayoutInflater.from(mContext));
when(mNssl.indexOfChild(any(View.class))).thenReturn(-1);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+
}
@Test(expected = IllegalStateException.class)
@@ -339,6 +360,58 @@
}
@Test
+ public void testPeopleFiltering_HunWhilePeopleVisible() {
+ enablePeopleFiltering();
+
+ setupMockStack(
+ ChildType.PEOPLE_HEADER,
+ ChildType.HEADS_UP,
+ ChildType.PERSON,
+ ChildType.ALERTING_HEADER,
+ ChildType.GENTLE_HEADER,
+ ChildType.GENTLE
+ );
+ mSectionsManager.updateSectionBoundaries();
+
+ verifyMockStack(
+ ChildType.INCOMING_HEADER,
+ ChildType.HEADS_UP,
+ ChildType.PEOPLE_HEADER,
+ ChildType.PERSON,
+ ChildType.GENTLE_HEADER,
+ ChildType.GENTLE
+ );
+ }
+
+ @Test
+ public void testPeopleFiltering_Fsn() {
+ enablePeopleFiltering();
+
+ setupMockStack(
+ ChildType.INCOMING_HEADER,
+ ChildType.HEADS_UP,
+ ChildType.PEOPLE_HEADER,
+ ChildType.FSN,
+ ChildType.PERSON,
+ ChildType.ALERTING,
+ ChildType.GENTLE
+ );
+ mSectionsManager.updateSectionBoundaries();
+
+ verifyMockStack(
+ ChildType.INCOMING_HEADER,
+ ChildType.HEADS_UP,
+ ChildType.FSN,
+ ChildType.PEOPLE_HEADER,
+ ChildType.PERSON,
+ ChildType.ALERTING_HEADER,
+ ChildType.ALERTING,
+ ChildType.GENTLE_HEADER,
+ ChildType.GENTLE
+ );
+ }
+
+ @Test
public void testMediaControls_AddWhenEnterKeyguard() {
enableMediaControls();
@@ -358,30 +431,28 @@
enableMediaControls();
// GIVEN a stack that doesn't include media controls but includes HEADS_UP
- setStackState(ChildType.HEADS_UP, ChildType.ALERTING, ChildType.GENTLE_HEADER,
+ setupMockStack(ChildType.HEADS_UP, ChildType.ALERTING, ChildType.GENTLE_HEADER,
ChildType.GENTLE);
// WHEN we go back to the keyguard
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
mSectionsManager.updateSectionBoundaries();
- // Then the media controls are added after HEADS_UP
- verify(mNssl).addView(mSectionsManager.getMediaControlsView(), 1);
+ verifyMockStack(ChildType.HEADS_UP, ChildType.MEDIA_CONTROLS, ChildType.ALERTING,
+ ChildType.GENTLE);
}
private void enablePeopleFiltering() {
when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
- when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4);
}
private void enableMediaControls() {
when(mSectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true);
- when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4);
}
private enum ChildType {
- MEDIA_CONTROLS, PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, PERSON, ALERTING,
- GENTLE, OTHER
+ INCOMING_HEADER, MEDIA_CONTROLS, PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP,
+ FSN, PERSON, ALERTING, GENTLE, OTHER
}
private void setStackState(ChildType... children) {
@@ -389,6 +460,9 @@
for (int i = 0; i < children.length; i++) {
View child;
switch (children[i]) {
+ case INCOMING_HEADER:
+ child = mSectionsManager.getIncomingHeaderView();
+ break;
case MEDIA_CONTROLS:
child = mSectionsManager.getMediaControlsView();
break;
@@ -404,6 +478,9 @@
case HEADS_UP:
child = mockNotification(BUCKET_HEADS_UP);
break;
+ case FSN:
+ child = mockNotification(BUCKET_FOREGROUND_SERVICE);
+ break;
case PERSON:
child = mockNotification(BUCKET_PEOPLE);
break;
@@ -434,4 +511,127 @@
when(notifRow.getParent()).thenReturn(mNssl);
return notifRow;
}
+
+ private void verifyMockStack(ChildType... expected) {
+ final List<ChildType> actual = new ArrayList<>();
+ int childCount = mNssl.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = mNssl.getChildAt(i);
+ if (child == mSectionsManager.getIncomingHeaderView()) {
+ actual.add(ChildType.INCOMING_HEADER);
+ continue;
+ }
+ if (child == mSectionsManager.getMediaControlsView()) {
+ actual.add(ChildType.MEDIA_CONTROLS);
+ continue;
+ }
+ if (child == mSectionsManager.getPeopleHeaderView()) {
+ actual.add(ChildType.PEOPLE_HEADER);
+ continue;
+ }
+ if (child == mSectionsManager.getAlertingHeaderView()) {
+ actual.add(ChildType.ALERTING_HEADER);
+ continue;
+ }
+ if (child == mSectionsManager.getGentleHeaderView()) {
+ actual.add(ChildType.GENTLE_HEADER);
+ continue;
+ }
+ if (child instanceof ExpandableNotificationRow) {
+ switch (((ExpandableNotificationRow) child).getEntry().getBucket()) {
+ case BUCKET_HEADS_UP:
+ actual.add(ChildType.HEADS_UP);
+ break;
+ case BUCKET_FOREGROUND_SERVICE:
+ actual.add(ChildType.FSN);
+ break;
+ case BUCKET_PEOPLE:
+ actual.add(ChildType.PERSON);
+ break;
+ case BUCKET_ALERTING:
+ actual.add(ChildType.ALERTING);
+ break;
+ case BUCKET_SILENT:
+ actual.add(ChildType.GENTLE);
+ break;
+ default:
+ actual.add(ChildType.OTHER);
+ break;
+ }
+ continue;
+ }
+ actual.add(ChildType.OTHER);
+ }
+ assertThat(actual).containsExactly((Object[]) expected).inOrder();
+ }
+
+ private void setupMockStack(ChildType... childTypes) {
+ final List<View> children = new ArrayList<>();
+ when(mNssl.getChildCount()).thenAnswer(invocation -> children.size());
+ when(mNssl.getChildAt(anyInt()))
+ .thenAnswer(invocation -> children.get(invocation.getArgument(0)));
+ when(mNssl.indexOfChild(any()))
+ .thenAnswer(invocation -> children.indexOf(invocation.getArgument(0)));
+ doAnswer(invocation -> {
+ View child = invocation.getArgument(0);
+ int index = invocation.getArgument(1);
+ children.add(index, child);
+ return null;
+ }).when(mNssl).addView(any(), anyInt());
+ doAnswer(invocation -> {
+ View child = invocation.getArgument(0);
+ children.remove(child);
+ return null;
+ }).when(mNssl).removeView(any());
+ doAnswer(invocation -> {
+ View child = invocation.getArgument(0);
+ int newIndex = invocation.getArgument(1);
+ children.remove(child);
+ children.add(newIndex, child);
+ return null;
+ }).when(mNssl).changeViewPosition(any(), anyInt());
+ for (ChildType childType : childTypes) {
+ View child;
+ switch (childType) {
+ case INCOMING_HEADER:
+ child = mSectionsManager.getIncomingHeaderView();
+ break;
+ case MEDIA_CONTROLS:
+ child = mSectionsManager.getMediaControlsView();
+ break;
+ case PEOPLE_HEADER:
+ child = mSectionsManager.getPeopleHeaderView();
+ break;
+ case ALERTING_HEADER:
+ child = mSectionsManager.getAlertingHeaderView();
+ break;
+ case GENTLE_HEADER:
+ child = mSectionsManager.getGentleHeaderView();
+ break;
+ case HEADS_UP:
+ child = mockNotification(BUCKET_HEADS_UP);
+ break;
+ case FSN:
+ child = mockNotification(BUCKET_FOREGROUND_SERVICE);
+ break;
+ case PERSON:
+ child = mockNotification(BUCKET_PEOPLE);
+ break;
+ case ALERTING:
+ child = mockNotification(BUCKET_ALERTING);
+ break;
+ case GENTLE:
+ child = mockNotification(BUCKET_SILENT);
+ break;
+ case OTHER:
+ child = mock(View.class);
+ when(child.getVisibility()).thenReturn(View.VISIBLE);
+ when(child.getParent()).thenReturn(mNssl);
+ break;
+ default:
+ throw new RuntimeException("Unknown ChildType: " + childType);
+ }
+ children.add(child);
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index e04d25b..cc2d1c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -83,7 +83,6 @@
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock private NotificationShadeDepthController mNotificationShadeDepthController;
@Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
- @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Before
public void setUp() {
@@ -122,7 +121,7 @@
mNotificationPanelViewController,
mStatusBarViewFactory);
mController.setupExpandedStatusBar();
- mController.setService(mStatusBar, mNotificationShadeWindowController);
+ mController.setService(mStatusBar);
mController.setDragDownHelper(mDragDownHelper);
}
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index cbc5e14..1ee017b 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -51,6 +51,11 @@
cc_library {
name: "libtetherutilsjni",
sdk_version: "current",
+ apex_available: [
+ "//apex_available:platform", // Used by InProcessTethering
+ "com.android.tethering",
+ ],
+ min_sdk_version: "current",
srcs: [
"jni/android_net_util_TetheringUtils.cpp",
],
@@ -110,6 +115,7 @@
// InProcessTethering is a replacement for Tethering
overrides: ["Tethering"],
apex_available: ["com.android.tethering"],
+ min_sdk_version: "current",
}
// Updatable tethering packaged as an application
@@ -123,4 +129,5 @@
// The permission configuration *must* be included to ensure security of the device
required: ["NetworkPermissionConfig"],
apex_available: ["com.android.tethering"],
+ min_sdk_version: "current",
}
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
index 20ccd2a..67097a7 100644
--- a/packages/Tethering/apex/Android.bp
+++ b/packages/Tethering/apex/Android.bp
@@ -17,7 +17,7 @@
apex {
name: "com.android.tethering",
updatable: true,
- min_sdk_version: "R",
+ min_sdk_version: "current",
java_libs: ["framework-tethering"],
apps: ["Tethering"],
manifest: "manifest.json",
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index d029d2b..ae4bb3e 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -13,43 +13,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// AIDL interfaces between the core system and the tethering mainline module.
-aidl_interface {
- name: "tethering-aidl-interfaces",
- unstable: true,
- local_include_dir: "src",
- include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
- srcs: [
- // @JavaOnlyStableParcelable aidl declarations must not be listed here, as this would cause
- // compilation to fail (b/148001843).
- "src/android/net/IIntResultListener.aidl",
- "src/android/net/ITetheringConnector.aidl",
- "src/android/net/ITetheringEventCallback.aidl",
- "src/android/net/TetheringCallbackStartedParcel.aidl",
- "src/android/net/TetheringConfigurationParcel.aidl",
- "src/android/net/TetheringRequestParcel.aidl",
- "src/android/net/TetherStatesParcel.aidl",
- ],
- backend: {
- ndk: {
- enabled: false,
- },
- cpp: {
- enabled: false,
- },
- },
-}
-
java_library {
name: "framework-tethering",
sdk_version: "module_current",
srcs: [
- "src/android/net/TetheredClient.java",
- "src/android/net/TetheringManager.java",
- "src/android/net/TetheringConstants.java",
- ],
- static_libs: [
- "tethering-aidl-interfaces-java",
+ ":framework-tethering-srcs",
],
jarjar_rules: "jarjar-rules.txt",
installable: true,
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 9dda716..9269c6f 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -57,6 +57,12 @@
<item>"bt-pan"</item>
</string-array>
+ <!-- Use the BPF offload for tethering when the kernel has support. True by default.
+ If the device doesn't want to support tether BPF offload, this should be false.
+ Note that this setting could be overridden by device config.
+ -->
+ <bool translatable="false" name="config_tether_enable_bpf_offload">true</bool>
+
<!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. -->
<bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool>
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
index 4c78a74..4e2bb1e 100644
--- a/packages/Tethering/res/values/overlayable.xml
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -23,6 +23,11 @@
<item type="array" name="config_tether_wifi_p2p_regexs"/>
<item type="array" name="config_tether_bluetooth_regexs"/>
<item type="array" name="config_tether_dhcp_range"/>
+ <!-- Use the BPF offload for tethering when the kernel has support. True by default.
+ If the device doesn't want to support tether BPF offload, this should be false.
+ Note that this setting could be overridden by device config.
+ -->
+ <item type="bool" name="config_tether_enable_bpf_offload"/>
<item type="bool" name="config_tether_enable_legacy_dhcp_server"/>
<item type="integer" name="config_tether_offload_poll_interval"/>
<item type="array" name="config_tether_upstream_types"/>
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 83727bc..d993306 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -227,6 +227,7 @@
private final int mInterfaceType;
private final LinkProperties mLinkProperties;
private final boolean mUsingLegacyDhcp;
+ private final boolean mUsingBpfOffload;
private final Dependencies mDeps;
@@ -302,9 +303,12 @@
private final IpNeighborMonitor mIpNeighborMonitor;
+ // TODO: Add a dependency object to pass the data members or variables from the tethering
+ // object. It helps to reduce the arguments of the constructor.
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
- INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
+ INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload,
+ Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNetd = netd;
@@ -314,6 +318,7 @@
mInterfaceType = interfaceType;
mLinkProperties = new LinkProperties();
mUsingLegacyDhcp = usingLegacyDhcp;
+ mUsingBpfOffload = usingBpfOffload;
mDeps = deps;
resetLinkProperties();
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -321,7 +326,12 @@
mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
new MyNeighborEventConsumer());
- if (!mIpNeighborMonitor.start()) {
+
+ // IP neighbor monitor monitors the neighbor events for adding/removing offload
+ // forwarding rules per client. If BPF offload is not supported, don't start listening
+ // for neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule,
+ // removeIpv6ForwardingRule.
+ if (mUsingBpfOffload && !mIpNeighborMonitor.start()) {
mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
}
@@ -715,12 +725,12 @@
final String upstreamIface = v6only.getInterfaceName();
params = new RaParams();
- // We advertise an mtu lower by 16, which is the closest multiple of 8 >= 14,
- // the ethernet header size. This makes kernel ebpf tethering offload happy.
- // This hack should be reverted once we have the kernel fixed up.
+ // When BPF offload is enabled, we advertise an mtu lower by 16, which is the closest
+ // multiple of 8 >= 14, the ethernet header size. This makes kernel ebpf tethering
+ // offload happy. This hack should be reverted once we have the kernel fixed up.
// Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu)
// see RouterAdvertisementDaemon.java putMtu()
- params.mtu = v6only.getMtu() - 16;
+ params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu();
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
@@ -844,6 +854,11 @@
}
private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
+ // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF
+ // offload is disabled. Add this check just in case.
+ // TODO: Perhaps remove this protection check.
+ if (!mUsingBpfOffload) return;
+
try {
mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
mIpv6ForwardingRules.put(rule.address, rule);
@@ -853,6 +868,11 @@
}
private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
+ // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF
+ // offload is disabled. Add this check just in case.
+ // TODO: Perhaps remove this protection check.
+ if (!mUsingBpfOffload) return;
+
try {
mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
if (removeFromMap) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index 293f8ea..fe92204 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -66,6 +66,7 @@
private final Handler mHandler;
private final SharedLog mLog;
+ private final Dependencies mDeps;
private IOffloadControl mOffloadControl;
private TetheringOffloadCallback mTetheringOffloadCallback;
private ControlCallback mControlCallback;
@@ -126,8 +127,76 @@
}
public OffloadHardwareInterface(Handler h, SharedLog log) {
+ this(h, log, new Dependencies(log));
+ }
+
+ OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) {
mHandler = h;
mLog = log.forSubComponent(TAG);
+ mDeps = deps;
+ }
+
+ /** Capture OffloadHardwareInterface dependencies, for injection. */
+ static class Dependencies {
+ private final SharedLog mLog;
+
+ Dependencies(SharedLog log) {
+ mLog = log;
+ }
+
+ public IOffloadConfig getOffloadConfig() {
+ try {
+ return IOffloadConfig.getService(true /*retry*/);
+ } catch (RemoteException | NoSuchElementException e) {
+ mLog.e("getIOffloadConfig error " + e);
+ return null;
+ }
+ }
+
+ public IOffloadControl getOffloadControl() {
+ try {
+ return IOffloadControl.getService(true /*retry*/);
+ } catch (RemoteException | NoSuchElementException e) {
+ mLog.e("tethering offload control not supported: " + e);
+ return null;
+ }
+ }
+
+ public NativeHandle createConntrackSocket(final int groups) {
+ final FileDescriptor fd;
+ try {
+ fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
+ } catch (ErrnoException e) {
+ mLog.e("Unable to create conntrack socket " + e);
+ return null;
+ }
+
+ final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
+ try {
+ Os.bind(fd, sockAddr);
+ } catch (ErrnoException | SocketException e) {
+ mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
+ try {
+ SocketUtils.closeSocket(fd);
+ } catch (IOException ie) {
+ // Nothing we can do here
+ }
+ return null;
+ }
+ try {
+ Os.connect(fd, sockAddr);
+ } catch (ErrnoException | SocketException e) {
+ mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
+ try {
+ SocketUtils.closeSocket(fd);
+ } catch (IOException ie) {
+ // Nothing we can do here
+ }
+ return null;
+ }
+
+ return new NativeHandle(fd, true);
+ }
}
/** Get default value indicating whether offload is supported. */
@@ -141,13 +210,7 @@
* share them with offload management process.
*/
public boolean initOffloadConfig() {
- IOffloadConfig offloadConfig;
- try {
- offloadConfig = IOffloadConfig.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
- mLog.e("getIOffloadConfig error " + e);
- return false;
- }
+ final IOffloadConfig offloadConfig = mDeps.getOffloadConfig();
if (offloadConfig == null) {
mLog.e("Could not find IOffloadConfig service");
return false;
@@ -159,11 +222,11 @@
//
// h2 provides a file descriptor bound to the following netlink groups
// (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
- final NativeHandle h1 = createConntrackSocket(
+ final NativeHandle h1 = mDeps.createConntrackSocket(
NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
if (h1 == null) return false;
- final NativeHandle h2 = createConntrackSocket(
+ final NativeHandle h2 = mDeps.createConntrackSocket(
NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
if (h2 == null) {
closeFdInNativeHandle(h1);
@@ -198,53 +261,12 @@
}
}
- private NativeHandle createConntrackSocket(final int groups) {
- FileDescriptor fd;
- try {
- fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
- } catch (ErrnoException e) {
- mLog.e("Unable to create conntrack socket " + e);
- return null;
- }
-
- final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
- try {
- Os.bind(fd, sockAddr);
- } catch (ErrnoException | SocketException e) {
- mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ie) {
- // Nothing we can do here
- }
- return null;
- }
- try {
- Os.connect(fd, sockAddr);
- } catch (ErrnoException | SocketException e) {
- mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ie) {
- // Nothing we can do here
- }
- return null;
- }
-
- return new NativeHandle(fd, true);
- }
-
/** Initialize the tethering offload HAL. */
public boolean initOffloadControl(ControlCallback controlCb) {
mControlCallback = controlCb;
if (mOffloadControl == null) {
- try {
- mOffloadControl = IOffloadControl.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
- mLog.e("tethering offload control not supported: " + e);
- return false;
- }
+ mOffloadControl = mDeps.getOffloadControl();
if (mOffloadControl == null) {
mLog.e("tethering IOffloadControl.getService() returned null");
return false;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index ae6119f2..04ad43f 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -109,8 +109,10 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceSpecificException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -228,6 +230,7 @@
private final ConnectedClientsTracker mConnectedClientsTracker;
private final TetheringThreadExecutor mExecutor;
private final TetheringNotificationUpdater mNotificationUpdater;
+ private final UserManager mUserManager;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
// All the usage of mTetheringEventCallback should run in the same thread.
private ITetheringEventCallback mTetheringEventCallback = null;
@@ -305,23 +308,24 @@
mStateReceiver = new StateReceiver();
- final UserManager userManager = (UserManager) mContext.getSystemService(
- Context.USER_SERVICE);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mTetheringRestriction = new UserRestrictionActionListener(
- userManager, this, mNotificationUpdater);
+ mUserManager, this, mNotificationUpdater);
mExecutor = new TetheringThreadExecutor(mHandler);
mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
mNetdCallback = new NetdCallback();
// Load tethering configuration.
updateConfiguration();
+
+ startStateMachineUpdaters();
}
/**
* Start to register callbacks.
* Call this function when tethering is ready to handle callback events.
*/
- public void startStateMachineUpdaters() {
+ private void startStateMachineUpdaters() {
try {
mNetd.registerUnsolicitedEventListener(mNetdCallback);
} catch (RemoteException e) {
@@ -779,7 +783,7 @@
// TODO: Figure out how to update for local hotspot mode interfaces.
private void sendTetherStateChangedBroadcast() {
- if (!mDeps.isTetheringSupported()) return;
+ if (!isTetheringSupported()) return;
final ArrayList<String> availableList = new ArrayList<>();
final ArrayList<String> tetherList = new ArrayList<>();
@@ -1020,14 +1024,14 @@
@VisibleForTesting
protected static class UserRestrictionActionListener {
- private final UserManager mUserManager;
+ private final UserManager mUserMgr;
private final Tethering mWrapper;
private final TetheringNotificationUpdater mNotificationUpdater;
public boolean mDisallowTethering;
public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper,
@NonNull TetheringNotificationUpdater updater) {
- mUserManager = um;
+ mUserMgr = um;
mWrapper = wrapper;
mNotificationUpdater = updater;
mDisallowTethering = false;
@@ -1037,7 +1041,7 @@
// getUserRestrictions gets restriction for this process' user, which is the primary
// user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary
// user. See UserManager.DISALLOW_CONFIG_TETHERING.
- final Bundle restrictions = mUserManager.getUserRestrictions();
+ final Bundle restrictions = mUserMgr.getUserRestrictions();
final boolean newlyDisallowed =
restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
final boolean prevDisallowed = mDisallowTethering;
@@ -1988,7 +1992,7 @@
mHandler.post(() -> {
mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
- parcel.tetheringSupported = mDeps.isTetheringSupported();
+ parcel.tetheringSupported = isTetheringSupported();
parcel.upstreamNetwork = mTetherUpstream;
parcel.config = mConfig.toStableParcelable();
parcel.states =
@@ -2111,6 +2115,20 @@
}
}
+ // if ro.tether.denied = true we default to no tethering
+ // gservices could set the secure setting to 1 though to enable it on a build where it
+ // had previously been turned off.
+ boolean isTetheringSupported() {
+ final int defaultVal =
+ SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1;
+ final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
+ final boolean tetherEnabledInSettings = tetherSupported
+ && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
+
+ return tetherEnabledInSettings && hasTetherableConfiguration();
+ }
+
void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
// Binder.java closes the resource for us.
@SuppressWarnings("resource")
@@ -2296,7 +2314,7 @@
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
- mDeps.getIpServerDependencies()));
+ mConfig.enableBpfOffload, mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 9d4e747..48a600d 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -73,6 +73,12 @@
private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
/**
+ * Override enabling BPF offload configuration for tethering.
+ */
+ public static final String OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD =
+ "override_tether_enable_bpf_offload";
+
+ /**
* Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
*/
public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
@@ -95,6 +101,8 @@
public final String[] legacyDhcpRanges;
public final String[] defaultIPv4DNS;
public final boolean enableLegacyDhcpServer;
+ // TODO: Add to TetheringConfigurationParcel if required.
+ public final boolean enableBpfOffload;
public final String[] provisioningApp;
public final String provisioningAppNoUi;
@@ -124,11 +132,12 @@
isDunRequired = checkDunRequired(ctx);
chooseUpstreamAutomatically = getResourceBoolean(
- res, R.bool.config_tether_upstream_automatic);
+ res, R.bool.config_tether_upstream_automatic, false /** defaultValue */);
preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
legacyDhcpRanges = getLegacyDhcpRanges(res);
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
+ enableBpfOffload = getEnableBpfOffload(res);
enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
@@ -208,6 +217,9 @@
pw.print("provisioningAppNoUi: ");
pw.println(provisioningAppNoUi);
+ pw.print("enableBpfOffload: ");
+ pw.println(enableBpfOffload);
+
pw.print("enableLegacyDhcpServer: ");
pw.println(enableLegacyDhcpServer);
}
@@ -228,6 +240,7 @@
toIntArray(preferredUpstreamIfaceTypes)));
sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
+ sj.add(String.format("enableBpfOffload:%s", enableBpfOffload));
sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer));
return String.format("TetheringConfiguration{%s}", sj.toString());
}
@@ -332,11 +345,11 @@
}
}
- private static boolean getResourceBoolean(Resources res, int resId) {
+ private static boolean getResourceBoolean(Resources res, int resId, boolean defaultValue) {
try {
return res.getBoolean(resId);
} catch (Resources.NotFoundException e404) {
- return false;
+ return defaultValue;
}
}
@@ -357,14 +370,36 @@
}
}
+ private boolean getEnableBpfOffload(final Resources res) {
+ // Get BPF offload config
+ // Priority 1: Device config
+ // Priority 2: Resource config
+ // Priority 3: Default value
+ final boolean defaultValue = getResourceBoolean(
+ res, R.bool.config_tether_enable_bpf_offload, true /** default value */);
+
+ return getDeviceConfigBoolean(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, defaultValue);
+ }
+
private boolean getEnableLegacyDhcpServer(final Resources res) {
- return getResourceBoolean(res, R.bool.config_tether_enable_legacy_dhcp_server)
- || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER);
+ return getResourceBoolean(
+ res, R.bool.config_tether_enable_legacy_dhcp_server, false /** defaultValue */)
+ || getDeviceConfigBoolean(
+ TETHER_ENABLE_LEGACY_DHCP_SERVER, false /** defaultValue */);
+ }
+
+ private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) {
+ // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead
+ // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the
+ // returned boolean value comes from device config or default value (because of null
+ // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java.
+ final String value = getDeviceConfigProperty(name);
+ return value != null ? Boolean.parseBoolean(value) : defaultValue;
}
@VisibleForTesting
- protected boolean getDeviceConfigBoolean(final String name) {
- return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, false /** defaultValue */);
+ protected String getDeviceConfigProperty(String name) {
+ return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name);
}
private Resources getResources(Context ctx, int subId) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index d07c555f..bf7fb04 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -40,15 +40,12 @@
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.ip.IpServer;
-import android.net.util.SharedLog;
import android.os.Binder;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.SystemProperties;
-import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
@@ -68,21 +65,14 @@
public class TetheringService extends Service {
private static final String TAG = TetheringService.class.getSimpleName();
- private final SharedLog mLog = new SharedLog(TAG);
private TetheringConnector mConnector;
- private Context mContext;
- private TetheringDependencies mDeps;
- private Tethering mTethering;
- private UserManager mUserManager;
@Override
public void onCreate() {
- mLog.mark("onCreate");
- mDeps = getTetheringDependencies();
- mContext = mDeps.getContext();
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mTethering = makeTethering(mDeps);
- mTethering.startStateMachineUpdaters();
+ final TetheringDependencies deps = makeTetheringDependencies();
+ // The Tethering object needs a fully functional context to start, so this can't be done
+ // in the constructor.
+ mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this);
}
/**
@@ -94,21 +84,10 @@
return new Tethering(deps);
}
- /**
- * Create a binder connector for the system server to communicate with the tethering.
- */
- private synchronized IBinder makeConnector() {
- if (mConnector == null) {
- mConnector = new TetheringConnector(mTethering, TetheringService.this);
- }
- return mConnector;
- }
-
@NonNull
@Override
public IBinder onBind(Intent intent) {
- mLog.mark("onBind");
- return makeConnector();
+ return mConnector;
}
private static class TetheringConnector extends ITetheringConnector.Stub {
@@ -237,7 +216,7 @@
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true;
}
- if (!mService.isTetheringSupported()) {
+ if (!mTethering.isTetheringSupported()) {
listener.onResult(TETHER_ERROR_UNSUPPORTED);
return true;
}
@@ -253,7 +232,7 @@
receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null);
return true;
}
- if (!mService.isTetheringSupported()) {
+ if (!mTethering.isTetheringSupported()) {
receiver.send(TETHER_ERROR_UNSUPPORTED, null);
return true;
}
@@ -287,105 +266,83 @@
}
}
- // if ro.tether.denied = true we default to no tethering
- // gservices could set the secure setting to 1 though to enable it on a build where it
- // had previously been turned off.
- private boolean isTetheringSupported() {
- final int defaultVal =
- SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1;
- final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
- final boolean tetherEnabledInSettings = tetherSupported
- && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
-
- return tetherEnabledInSettings && mTethering.hasTetherableConfiguration();
- }
-
/**
* An injection method for testing.
*/
@VisibleForTesting
- public TetheringDependencies getTetheringDependencies() {
- if (mDeps == null) {
- mDeps = new TetheringDependencies() {
- @Override
- public NetworkRequest getDefaultNetworkRequest() {
- // TODO: b/147280869, add a proper system API to replace this.
- final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder()
- .clearCapabilities()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build();
- return trackDefaultRequest;
- }
+ public TetheringDependencies makeTetheringDependencies() {
+ return new TetheringDependencies() {
+ @Override
+ public NetworkRequest getDefaultNetworkRequest() {
+ // TODO: b/147280869, add a proper system API to replace this.
+ final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ return trackDefaultRequest;
+ }
- @Override
- public Looper getTetheringLooper() {
- final HandlerThread tetherThread = new HandlerThread("android.tethering");
- tetherThread.start();
- return tetherThread.getLooper();
- }
+ @Override
+ public Looper getTetheringLooper() {
+ final HandlerThread tetherThread = new HandlerThread("android.tethering");
+ tetherThread.start();
+ return tetherThread.getLooper();
+ }
- @Override
- public boolean isTetheringSupported() {
- return TetheringService.this.isTetheringSupported();
- }
+ @Override
+ public Context getContext() {
+ return TetheringService.this;
+ }
- @Override
- public Context getContext() {
- return TetheringService.this;
- }
+ @Override
+ public IpServer.Dependencies getIpServerDependencies() {
+ return new IpServer.Dependencies() {
+ @Override
+ public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+ DhcpServerCallbacks cb) {
+ try {
+ final INetworkStackConnector service = getNetworkStackConnector();
+ if (service == null) return;
- @Override
- public IpServer.Dependencies getIpServerDependencies() {
- return new IpServer.Dependencies() {
- @Override
- public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
- DhcpServerCallbacks cb) {
+ service.makeDhcpServer(ifName, params, cb);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Fail to make dhcp server");
try {
- final INetworkStackConnector service = getNetworkStackConnector();
- if (service == null) return;
-
- service.makeDhcpServer(ifName, params, cb);
- } catch (RemoteException e) {
- Log.e(TAG, "Fail to make dhcp server");
- try {
- cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
- } catch (RemoteException re) { }
- }
+ cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
+ } catch (RemoteException re) { }
}
- };
- }
-
- // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring
- // networkStackClient.
- static final int NETWORKSTACK_TIMEOUT_MS = 60_000;
- private INetworkStackConnector getNetworkStackConnector() {
- IBinder connector;
- try {
- final long before = System.currentTimeMillis();
- while ((connector = NetworkStack.getService()) == null) {
- if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
- Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
- return null;
- }
- Thread.sleep(200);
- }
- } catch (InterruptedException e) {
- Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector");
- return null;
}
- return INetworkStackConnector.Stub.asInterface(connector);
- }
+ };
+ }
- @Override
- public BluetoothAdapter getBluetoothAdapter() {
- return BluetoothAdapter.getDefaultAdapter();
+ // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring
+ // networkStackClient.
+ static final int NETWORKSTACK_TIMEOUT_MS = 60_000;
+ private INetworkStackConnector getNetworkStackConnector() {
+ IBinder connector;
+ try {
+ final long before = System.currentTimeMillis();
+ while ((connector = NetworkStack.getService()) == null) {
+ if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+ Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
+ return null;
+ }
+ Thread.sleep(200);
+ }
+ } catch (InterruptedException e) {
+ Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector");
+ return null;
}
- };
- }
- return mDeps;
+ return INetworkStackConnector.Stub.asInterface(connector);
+ }
+
+ @Override
+ public BluetoothAdapter getBluetoothAdapter() {
+ return BluetoothAdapter.getDefaultAdapter();
+ }
+ };
}
}
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index f9be7b9..b9622da 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -106,6 +106,7 @@
private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
private static final int DHCP_LEASE_TIME_SECS = 3600;
+ private static final boolean DEFAULT_USING_BPF_OFFLOAD = true;
private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
@@ -130,10 +131,11 @@
private NeighborEventConsumer mNeighborEventConsumer;
private void initStateMachine(int interfaceType) throws Exception {
- initStateMachine(interfaceType, false /* usingLegacyDhcp */);
+ initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD);
}
- private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
+ private void initStateMachine(int interfaceType, boolean usingLegacyDhcp,
+ boolean usingBpfOffload) throws Exception {
doAnswer(inv -> {
final IDhcpServerCallbacks cb = inv.getArgument(2);
new Thread(() -> {
@@ -165,7 +167,7 @@
mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
- mCallback, usingLegacyDhcp, mDependencies);
+ mCallback, usingLegacyDhcp, usingBpfOffload, mDependencies);
mIpServer.start();
mNeighborEventConsumer = neighborCaptor.getValue();
@@ -179,12 +181,13 @@
private void initTetheredStateMachine(int interfaceType, String upstreamIface)
throws Exception {
- initTetheredStateMachine(interfaceType, upstreamIface, false);
+ initTetheredStateMachine(interfaceType, upstreamIface, false,
+ DEFAULT_USING_BPF_OFFLOAD);
}
private void initTetheredStateMachine(int interfaceType, String upstreamIface,
- boolean usingLegacyDhcp) throws Exception {
- initStateMachine(interfaceType, usingLegacyDhcp);
+ boolean usingLegacyDhcp, boolean usingBpfOffload) throws Exception {
+ initStateMachine(interfaceType, usingLegacyDhcp, usingBpfOffload);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
if (upstreamIface != null) {
LinkProperties lp = new LinkProperties();
@@ -204,7 +207,8 @@
when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
.thenReturn(mIpNeighborMonitor);
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
- mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
+ mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD,
+ mDependencies);
mIpServer.start();
mLooper.dispatchAll();
verify(mCallback).updateInterfaceState(
@@ -494,7 +498,8 @@
@Test
public void doesNotStartDhcpServerIfDisabled() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */,
+ DEFAULT_USING_BPF_OFFLOAD);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
@@ -577,7 +582,8 @@
@Test
public void addRemoveipv6ForwardingRules() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */);
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
+ DEFAULT_USING_BPF_OFFLOAD);
final int myIfindex = TEST_IFACE_PARAMS.index;
final int notMyIfindex = myIfindex - 1;
@@ -678,6 +684,53 @@
reset(mNetd);
}
+ @Test
+ public void enableDisableUsingBpfOffload() throws Exception {
+ final int myIfindex = TEST_IFACE_PARAMS.index;
+ final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1");
+ final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
+ final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
+
+ reset(mNetd);
+
+ // Expect that rules can be only added/removed when the BPF offload config is enabled.
+ // Note that the usingBpfOffload false case is not a realistic test case. Because IP
+ // neighbor monitor doesn't start if BPF offload is disabled, there should have no
+ // neighbor event listening. This is used for testing the protection check just in case.
+ // TODO: Perhaps remove this test once we don't need this check anymore.
+ for (boolean usingBpfOffload : new boolean[]{true, false}) {
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
+ usingBpfOffload);
+
+ // A neighbor is added.
+ recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
+ if (usingBpfOffload) {
+ verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA));
+ } else {
+ verify(mNetd, never()).tetherOffloadRuleAdd(any());
+ }
+ reset(mNetd);
+
+ // A neighbor is deleted.
+ recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
+ if (usingBpfOffload) {
+ verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull));
+ } else {
+ verify(mNetd, never()).tetherOffloadRuleRemove(any());
+ }
+ reset(mNetd);
+ }
+ }
+
+ @Test
+ public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception {
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
+ false /* usingBpfOffload */);
+
+ // IP neighbor monitor doesn't start if BPF offload is disabled.
+ verify(mIpNeighborMonitor, never()).start();
+ }
+
private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index cdd0e24..72fa916 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -147,9 +147,8 @@
doReturn(false).when(
() -> SystemProperties.getBoolean(
eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean()));
- doReturn(false).when(
- () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+ doReturn(null).when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), anyString()));
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
new file mode 100644
index 0000000..f8ff1cb
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.networkstack.tethering;
+
+import static android.net.util.TetheringUtils.uint16;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
+import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
+import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
+import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
+import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
+import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.net.util.SharedLog;
+import android.os.Handler;
+import android.os.NativeHandle;
+import android.os.test.TestLooper;
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class OffloadHardwareInterfaceTest {
+ private static final String RMNET0 = "test_rmnet_data0";
+
+ private final TestLooper mTestLooper = new TestLooper();
+
+ private OffloadHardwareInterface mOffloadHw;
+ private ITetheringOffloadCallback mTetheringOffloadCallback;
+ private OffloadHardwareInterface.ControlCallback mControlCallback;
+
+ @Mock private IOffloadConfig mIOffloadConfig;
+ @Mock private IOffloadControl mIOffloadControl;
+ @Mock private NativeHandle mNativeHandle;
+
+ class MyDependencies extends OffloadHardwareInterface.Dependencies {
+ MyDependencies(SharedLog log) {
+ super(log);
+ }
+
+ @Override
+ public IOffloadConfig getOffloadConfig() {
+ return mIOffloadConfig;
+ }
+
+ @Override
+ public IOffloadControl getOffloadControl() {
+ return mIOffloadControl;
+ }
+
+ @Override
+ public NativeHandle createConntrackSocket(final int groups) {
+ return mNativeHandle;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ final SharedLog log = new SharedLog("test");
+ mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
+ new MyDependencies(log));
+ mControlCallback = spy(new OffloadHardwareInterface.ControlCallback());
+ }
+
+ private void startOffloadHardwareInterface() throws Exception {
+ mOffloadHw.initOffloadConfig();
+ mOffloadHw.initOffloadControl(mControlCallback);
+ final ArgumentCaptor<ITetheringOffloadCallback> mOffloadCallbackCaptor =
+ ArgumentCaptor.forClass(ITetheringOffloadCallback.class);
+ verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any());
+ mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue();
+ }
+
+ @Test
+ public void testGetForwardedStats() throws Exception {
+ startOffloadHardwareInterface();
+ final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0);
+ verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any());
+ assertNotNull(stats);
+ }
+
+ @Test
+ public void testSetLocalPrefixes() throws Exception {
+ startOffloadHardwareInterface();
+ final ArrayList<String> localPrefixes = new ArrayList<>();
+ localPrefixes.add("127.0.0.0/8");
+ localPrefixes.add("fe80::/64");
+ mOffloadHw.setLocalPrefixes(localPrefixes);
+ verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any());
+ }
+
+ @Test
+ public void testSetDataLimit() throws Exception {
+ startOffloadHardwareInterface();
+ final long limit = 12345;
+ mOffloadHw.setDataLimit(RMNET0, limit);
+ verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any());
+ }
+
+ @Test
+ public void testSetUpstreamParameters() throws Exception {
+ startOffloadHardwareInterface();
+ final String v4addr = "192.168.10.1";
+ final String v4gateway = "192.168.10.255";
+ final ArrayList<String> v6gws = new ArrayList<>(0);
+ v6gws.add("2001:db8::1");
+ mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws);
+ verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway),
+ eq(v6gws), any());
+
+ final ArgumentCaptor<ArrayList<String>> mArrayListCaptor =
+ ArgumentCaptor.forClass(ArrayList.class);
+ mOffloadHw.setUpstreamParameters(null, null, null, null);
+ verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""),
+ mArrayListCaptor.capture(), any());
+ assertEquals(mArrayListCaptor.getValue().size(), 0);
+ }
+
+ @Test
+ public void testUpdateDownstreamPrefix() throws Exception {
+ startOffloadHardwareInterface();
+ final String ifName = "wlan1";
+ final String prefix = "192.168.43.0/24";
+ mOffloadHw.addDownstreamPrefix(ifName, prefix);
+ verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any());
+
+ mOffloadHw.removeDownstreamPrefix(ifName, prefix);
+ verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any());
+ }
+
+ @Test
+ public void testTetheringOffloadCallback() throws Exception {
+ startOffloadHardwareInterface();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onStarted();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onStoppedError();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onStoppedUnsupported();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onSupportAvailable();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onStoppedLimitReached();
+
+ final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP);
+ mTetheringOffloadCallback.updateTimeout(tcpParams);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP),
+ eq(tcpParams.src.addr),
+ eq(uint16(tcpParams.src.port)),
+ eq(tcpParams.dst.addr),
+ eq(uint16(tcpParams.dst.port)));
+
+ final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP);
+ mTetheringOffloadCallback.updateTimeout(udpParams);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP),
+ eq(udpParams.src.addr),
+ eq(uint16(udpParams.src.port)),
+ eq(udpParams.dst.addr),
+ eq(uint16(udpParams.dst.port)));
+ }
+
+ private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) {
+ final NatTimeoutUpdate params = new NatTimeoutUpdate();
+ params.proto = proto;
+ params.src.addr = "192.168.43.200";
+ params.src.port = 100;
+ params.dst.addr = "172.50.46.169";
+ params.dst.port = 150;
+ return params;
+ }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index e8ba5b8..1999ad7 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -30,7 +30,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
@@ -109,9 +108,9 @@
.mockStatic(DeviceConfig.class)
.strictness(Strictness.WARN)
.startMocking();
- doReturn(false).when(
- () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+ doReturn(null).when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn(
new String[0]);
@@ -127,6 +126,8 @@
.thenReturn(new String[0]);
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
+ initializeBpfOffloadConfiguration(true, null /* unset */);
+
mHasTelephonyManager = true;
mMockContext = new MockContext(mContext);
mEnableLegacyDhcpServer = false;
@@ -278,13 +279,57 @@
assertFalse(upstreamIterator.hasNext());
}
+ private void initializeBpfOffloadConfiguration(
+ final boolean fromRes, final String fromDevConfig) {
+ when(mResources.getBoolean(R.bool.config_tether_enable_bpf_offload)).thenReturn(fromRes);
+ doReturn(fromDevConfig).when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD)));
+ }
+
+ @Test
+ public void testBpfOffloadEnabledByResource() {
+ initializeBpfOffloadConfiguration(true, null /* unset */);
+ final TetheringConfiguration enableByRes =
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ assertTrue(enableByRes.enableBpfOffload);
+ }
+
+ @Test
+ public void testBpfOffloadEnabledByDeviceConfigOverride() {
+ for (boolean res : new boolean[]{true, false}) {
+ initializeBpfOffloadConfiguration(res, "true");
+ final TetheringConfiguration enableByDevConOverride =
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ assertTrue(enableByDevConOverride.enableBpfOffload);
+ }
+ }
+
+ @Test
+ public void testBpfOffloadDisabledByResource() {
+ initializeBpfOffloadConfiguration(false, null /* unset */);
+ final TetheringConfiguration disableByRes =
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ assertFalse(disableByRes.enableBpfOffload);
+ }
+
+ @Test
+ public void testBpfOffloadDisabledByDeviceConfigOverride() {
+ for (boolean res : new boolean[]{true, false}) {
+ initializeBpfOffloadConfiguration(res, "false");
+ final TetheringConfiguration disableByDevConOverride =
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ assertFalse(disableByDevConOverride.enableBpfOffload);
+ }
+ }
+
@Test
public void testNewDhcpServerDisabled() {
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
true);
- doReturn(false).when(
- () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+ doReturn("false").when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
final TetheringConfiguration enableByRes =
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -292,9 +337,9 @@
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
- doReturn(true).when(
- () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+ doReturn("true").when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
final TetheringConfiguration enableByDevConfig =
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -305,9 +350,9 @@
public void testNewDhcpServerEnabled() {
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
- doReturn(false).when(
- () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+ doReturn("false").when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
final TetheringConfiguration cfg =
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index 51bad9a..4a667b1 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -82,8 +82,7 @@
mTetheringConnector = mockConnector.getTetheringConnector();
final MockTetheringService service = mockConnector.getService();
mTethering = service.getTethering();
- verify(mTethering).startStateMachineUpdaters();
- when(mTethering.hasTetherableConfiguration()).thenReturn(true);
+ when(mTethering.isTetheringSupported()).thenReturn(true);
}
@After
@@ -96,7 +95,7 @@
when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
final TestTetheringResult result = new TestTetheringResult();
mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, result);
- verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).isTetheringSupported();
verify(mTethering).tether(TEST_IFACE_NAME);
verifyNoMoreInteractions(mTethering);
result.assertResult(TETHER_ERROR_NO_ERROR);
@@ -107,7 +106,7 @@
when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
final TestTetheringResult result = new TestTetheringResult();
mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, result);
- verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).isTetheringSupported();
verify(mTethering).untether(TEST_IFACE_NAME);
verifyNoMoreInteractions(mTethering);
result.assertResult(TETHER_ERROR_NO_ERROR);
@@ -118,7 +117,7 @@
when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR);
final TestTetheringResult result = new TestTetheringResult();
mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, result);
- verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).isTetheringSupported();
verify(mTethering).setUsbTethering(true /* enable */);
verifyNoMoreInteractions(mTethering);
result.assertResult(TETHER_ERROR_NO_ERROR);
@@ -130,7 +129,7 @@
final TetheringRequestParcel request = new TetheringRequestParcel();
request.tetheringType = TETHERING_WIFI;
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result);
- verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).isTetheringSupported();
verify(mTethering).startTethering(eq(request), eq(result));
verifyNoMoreInteractions(mTethering);
}
@@ -139,7 +138,7 @@
public void testStopTethering() throws Exception {
final TestTetheringResult result = new TestTetheringResult();
mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, result);
- verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).isTetheringSupported();
verify(mTethering).stopTethering(TETHERING_WIFI);
verifyNoMoreInteractions(mTethering);
result.assertResult(TETHER_ERROR_NO_ERROR);
@@ -150,7 +149,7 @@
final ResultReceiver result = new ResultReceiver(null);
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
true /* showEntitlementUi */, TEST_CALLER_PKG);
- verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).isTetheringSupported();
verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI),
eq(result), eq(true) /* showEntitlementUi */);
verifyNoMoreInteractions(mTethering);
@@ -177,7 +176,7 @@
public void testStopAllTethering() throws Exception {
final TestTetheringResult result = new TestTetheringResult();
mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, result);
- verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).isTetheringSupported();
verify(mTethering).untetherAll();
verifyNoMoreInteractions(mTethering);
result.assertResult(TETHER_ERROR_NO_ERROR);
@@ -187,7 +186,7 @@
public void testIsTetheringSupported() throws Exception {
final TestTetheringResult result = new TestTetheringResult();
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, result);
- verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).isTetheringSupported();
verifyNoMoreInteractions(mTethering);
result.assertResult(TETHER_ERROR_NO_ERROR);
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 2bd8ae0..0132aba 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -312,8 +312,8 @@
}
@Override
- protected boolean getDeviceConfigBoolean(final String name) {
- return false;
+ protected String getDeviceConfigProperty(final String name) {
+ return null;
}
@Override
@@ -485,18 +485,6 @@
MockitoAnnotations.initMocks(this);
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
- when(mResources.getStringArray(R.array.config_tether_usb_regexs))
- .thenReturn(new String[] { "test_rndis\\d" });
- when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
- .thenReturn(new String[]{ "test_wlan\\d" });
- when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
- .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
- when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
- when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
- .thenReturn(new String[] { "test_ncm\\d" });
- when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
- when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false);
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
when(mNetd.interfaceGetList())
@@ -515,6 +503,7 @@
mServiceContext = new TestContext(mContext);
mContentResolver = new MockContentResolver(mServiceContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ setTetheringSupported(true /* supported */);
mIntents = new Vector<>();
mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -525,7 +514,6 @@
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ACTION_TETHER_STATE_CHANGED));
mTethering = makeTethering();
- mTethering.startStateMachineUpdaters();
verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
verify(mNetd).registerUnsolicitedEventListener(any());
final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor =
@@ -536,6 +524,31 @@
mPhoneStateListener = phoneListenerCaptor.getValue();
}
+ private void setTetheringSupported(final boolean supported) {
+ Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED,
+ supported ? 1 : 0);
+ when(mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported);
+ // Setup tetherable configuration.
+ when(mResources.getStringArray(R.array.config_tether_usb_regexs))
+ .thenReturn(new String[] { "test_rndis\\d" });
+ when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
+ .thenReturn(new String[]{ "test_wlan\\d" });
+ when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
+ .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
+ when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
+ .thenReturn(new String[] { "test_ncm\\d" });
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
+ when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
+ }
+
+ private void initTetheringUpstream(UpstreamNetworkState upstreamState) {
+ when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
+ when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+ }
+
private Tethering makeTethering() {
mTetheringDependencies.reset();
return new Tethering(mTetheringDependencies);
@@ -672,9 +685,7 @@
}
private void prepareUsbTethering(UpstreamNetworkState upstreamState) {
- when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
- .thenReturn(upstreamState);
+ initTetheringUpstream(upstreamState);
// Emulate pressing the USB tethering button in Settings UI.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null);
@@ -700,7 +711,7 @@
verify(mNetd, times(1)).interfaceGetList();
// UpstreamNetworkMonitor should receive selected upstream
- verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any());
+ verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream();
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
}
@@ -872,8 +883,7 @@
// Then 464xlat comes up
upstreamState = buildMobile464xlatUpstreamState();
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
- .thenReturn(upstreamState);
+ initTetheringUpstream(upstreamState);
// Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage(
@@ -1344,9 +1354,7 @@
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
// 2. Enable wifi tethering.
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
- when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
- .thenReturn(upstreamState);
+ initTetheringUpstream(upstreamState);
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
mLooper.dispatchAll();
@@ -1723,7 +1731,7 @@
final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+ initTetheringUpstream(upstreamState);
stateMachine.chooseUpstreamType(true);
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network));
@@ -1735,7 +1743,7 @@
final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+ initTetheringUpstream(upstreamState);
stateMachine.chooseUpstreamType(true);
stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2e29a43..e9cf727 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1544,6 +1544,7 @@
} else {
if (service != null) {
service.unbindLocked();
+ removeShortcutTargetForUnboundServiceLocked(userState, service);
}
}
}
@@ -2318,6 +2319,36 @@
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
}
+ /**
+ * Remove the shortcut target for the unbound service which is requesting accessibility button
+ * and targeting sdk > Q from the accessibility button and shortcut.
+ *
+ * @param userState The accessibility user state.
+ * @param service The unbound service.
+ */
+ private void removeShortcutTargetForUnboundServiceLocked(AccessibilityUserState userState,
+ AccessibilityServiceConnection service) {
+ if (!service.mRequestAccessibilityButton
+ || service.getServiceInfo().getResolveInfo().serviceInfo.applicationInfo
+ .targetSdkVersion <= Build.VERSION_CODES.Q) {
+ return;
+ }
+ final ComponentName serviceName = service.getComponentName();
+ if (userState.removeShortcutTargetLocked(ACCESSIBILITY_SHORTCUT_KEY, serviceName)) {
+ final Set<String> currentTargets = userState.getShortcutTargetsLocked(
+ ACCESSIBILITY_SHORTCUT_KEY);
+ persistColonDelimitedSetToSettingLocked(
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ userState.mUserId, currentTargets, str -> str);
+ }
+ if (userState.removeShortcutTargetLocked(ACCESSIBILITY_BUTTON, serviceName)) {
+ final Set<String> currentTargets = userState.getShortcutTargetsLocked(
+ ACCESSIBILITY_BUTTON);
+ persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ userState.mUserId, currentTargets, str -> str);
+ }
+ }
+
private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) {
int newNonInteractiveUiTimeout = userState.getUserNonInteractiveUiTimeoutLocked();
int newInteractiveUiTimeout = userState.getUserInteractiveUiTimeoutLocked();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index bad649a..43bb4b3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -631,6 +631,25 @@
}
/**
+ * Removes given shortcut target in the list.
+ *
+ * @param shortcutType The shortcut type.
+ * @param target The component name of the shortcut target.
+ * @return true if the shortcut target is removed.
+ */
+ public boolean removeShortcutTargetLocked(@ShortcutType int shortcutType,
+ ComponentName target) {
+ return getShortcutTargetsLocked(shortcutType).removeIf(name -> {
+ ComponentName componentName;
+ if (name == null
+ || (componentName = ComponentName.unflattenFromString(name)) == null) {
+ return false;
+ }
+ return componentName.equals(target);
+ });
+ }
+
+ /**
* Returns installed accessibility service info by the given service component name.
*/
public AccessibilityServiceInfo getInstalledServiceInfoLocked(ComponentName componentName) {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
index a14584a..4b89731 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
@@ -20,9 +20,9 @@
import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM;
import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex;
-import static com.android.server.accessibility.gestures.Swipe.CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS;
-import static com.android.server.accessibility.gestures.Swipe.CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS;
import static com.android.server.accessibility.gestures.Swipe.GESTURE_CONFIRM_CM;
+import static com.android.server.accessibility.gestures.Swipe.MAX_TIME_TO_CONTINUE_SWIPE_MS;
+import static com.android.server.accessibility.gestures.Swipe.MAX_TIME_TO_START_SWIPE_MS;
import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
import android.content.Context;
@@ -387,10 +387,10 @@
cancelPendingTransitions();
switch (getState()) {
case STATE_CLEAR:
- cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS, event, rawEvent, policyFlags);
+ cancelAfter(MAX_TIME_TO_START_SWIPE_MS, event, rawEvent, policyFlags);
break;
case STATE_GESTURE_STARTED:
- cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS, event, rawEvent, policyFlags);
+ cancelAfter(MAX_TIME_TO_CONTINUE_SWIPE_MS, event, rawEvent, policyFlags);
break;
default:
break;
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
index 9108c69..041b424 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
@@ -49,11 +49,10 @@
// Buffer for storing points for gesture detection.
private final ArrayList<PointF> mStrokeBuffer = new ArrayList<>(100);
- // The minimal delta between moves to add a gesture point.
- private static final int TOUCH_TOLERANCE_PIX = 3;
-
- // The minimal score for accepting a predicted gesture.
- private static final float MIN_PREDICTION_SCORE = 2.0f;
+ // Constants for sampling motion event points.
+ // We sample based on a minimum distance between points, primarily to improve accuracy by
+ // reducing noisy minor changes in direction.
+ private static final float MIN_CM_BETWEEN_SAMPLES = 0.25f;
// Distance a finger must travel before we decide if it is a gesture or not.
public static final int GESTURE_CONFIRM_CM = 1;
@@ -67,22 +66,19 @@
// all gestures started with the initial movement taking less than 100ms.
// When touch exploring, the first movement almost always takes longer than
// 200ms.
- public static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
+ public static final long MAX_TIME_TO_START_SWIPE_MS = 150 * GESTURE_CONFIRM_CM;
// Time threshold used to determine if a gesture should be cancelled. If
- // the finger takes more than this time to move 1cm, the ongoing gesture is
- // cancelled.
- public static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 300;
+ // the finger takes more than this time to move to the next sample point, the ongoing gesture
+ // is cancelled.
+ public static final long MAX_TIME_TO_CONTINUE_SWIPE_MS = 350 * GESTURE_CONFIRM_CM;
private int[] mDirections;
private float mBaseX;
private float mBaseY;
+ private long mBaseTime;
private float mPreviousGestureX;
private float mPreviousGestureY;
- // Constants for sampling motion event points.
- // We sample based on a minimum distance between points, primarily to improve accuracy by
- // reducing noisy minor changes in direction.
- private static final float MIN_CM_BETWEEN_SAMPLES = 0.25f;
private final float mMinPixelsBetweenSamplesX;
private final float mMinPixelsBetweenSamplesY;
// The minmimum distance the finger must travel before we evaluate the initial direction of the
@@ -134,16 +130,19 @@
protected void clear() {
mBaseX = Float.NaN;
mBaseY = Float.NaN;
+ mBaseTime = 0;
+ mPreviousGestureX = Float.NaN;
+ mPreviousGestureY = Float.NaN;
mStrokeBuffer.clear();
super.clear();
}
@Override
protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- cancelAfterPauseThreshold(event, rawEvent, policyFlags);
if (Float.isNaN(mBaseX) && Float.isNaN(mBaseY)) {
mBaseX = rawEvent.getX();
mBaseY = rawEvent.getY();
+ mBaseTime = rawEvent.getEventTime();
mPreviousGestureX = mBaseX;
mPreviousGestureY = mBaseY;
}
@@ -154,9 +153,11 @@
protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
final float x = rawEvent.getX();
final float y = rawEvent.getY();
+ final long time = rawEvent.getEventTime();
final float dX = Math.abs(x - mPreviousGestureX);
final float dY = Math.abs(y - mPreviousGestureY);
final double moveDelta = Math.hypot(Math.abs(x - mBaseX), Math.abs(y - mBaseY));
+ final long timeDelta = time - mBaseTime;
if (DEBUG) {
Slog.d(
getGestureName(),
@@ -171,34 +172,38 @@
return;
} else if (mStrokeBuffer.size() == 0) {
// First, make sure the pointer is going in the right direction.
- cancelAfterPauseThreshold(event, rawEvent, policyFlags);
int direction = toDirection(x - mBaseX, y - mBaseY);
if (direction != mDirections[0]) {
cancelGesture(event, rawEvent, policyFlags);
return;
- } else {
- // This is confirmed to be some kind of swipe so start tracking points.
- mStrokeBuffer.add(new PointF(mBaseX, mBaseY));
}
- }
- if (moveDelta > mGestureDetectionThresholdPixels) {
- // If the pointer has moved more than the threshold,
- // update the stored values.
- mBaseX = x;
- mBaseY = y;
- if (getState() == STATE_CLEAR) {
- startGesture(event, rawEvent, policyFlags);
- cancelAfterPauseThreshold(event, rawEvent, policyFlags);
- }
+ // This is confirmed to be some kind of swipe so start tracking points.
+ mStrokeBuffer.add(new PointF(mBaseX, mBaseY));
}
}
- if (getState() == STATE_GESTURE_STARTED) {
- if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
- mPreviousGestureX = x;
- mPreviousGestureY = y;
- mStrokeBuffer.add(new PointF(x, y));
- cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ if (moveDelta > mGestureDetectionThresholdPixels) {
+ // This is a gesture, not touch exploration.
+ mBaseX = x;
+ mBaseY = y;
+ mBaseTime = time;
+ startGesture(event, rawEvent, policyFlags);
+ } else if (getState() == STATE_CLEAR) {
+ if (timeDelta > MAX_TIME_TO_START_SWIPE_MS) {
+ // The user isn't moving fast enough.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
}
+ } else if (getState() == STATE_GESTURE_STARTED) {
+ if (timeDelta > MAX_TIME_TO_CONTINUE_SWIPE_MS) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ }
+ if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+ // At this point gesture detection has started and we are sampling points.
+ mPreviousGestureX = x;
+ mPreviousGestureY = y;
+ mStrokeBuffer.add(new PointF(x, y));
}
}
@@ -230,25 +235,6 @@
}
/**
- * queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have
- * transitioned to STATE_GESTURE_STARTED the delay is longer.
- */
- private void cancelAfterPauseThreshold(
- MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- cancelPendingTransitions();
- switch (getState()) {
- case STATE_CLEAR:
- cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS, event, rawEvent, policyFlags);
- break;
- case STATE_GESTURE_STARTED:
- cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS, event, rawEvent, policyFlags);
- break;
- default:
- break;
- }
- }
-
- /**
* Looks at the sequence of motions in mStrokeBuffer, classifies the gesture, then calls
* Listener callbacks for success or failure.
*
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index fdc5f81..103151d 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -267,6 +267,9 @@
mRemoteService.destroy();
mRemoteService = null;
+ synchronized (mLock) {
+ mZombie = true;
+ }
mRemoteService = getRemoteServiceLocked();
if (mRemoteService != null) {
if (isDebug()) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
index 3282870..3dd2433 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -67,8 +67,8 @@
* Requests the IME to create an {@link InlineSuggestionsRequest} for {@code autofillId}.
*
* @param autofillId the Id of the field for which the request is for.
- * @param requestConsumer the callback which will be invoked when IME responded or if it times
- * out waiting for IME response.
+ * @param requestConsumer the callback to be invoked when the IME responds. Note that this is
+ * never invoked if the IME doesn't respond.
*/
@GuardedBy("mLock")
void onCreateInlineSuggestionsRequestLocked(@NonNull AutofillId autofillId,
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 0bf8993..1a3baba 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -55,16 +55,6 @@
private static final String TAG = AutofillInlineSuggestionsRequestSession.class.getSimpleName();
- // This timeout controls how long Autofill should wait for the IME to respond either
- // unsupported or an {@link InlineSuggestionsRequest}. The timeout is needed to take into
- // account the latency between the two events after a field is focused, 1) an Autofill
- // request is triggered on framework; 2) the InputMethodService#onStartInput() event is
- // triggered on the IME side. When 1) happens, Autofill may call the IME to return an {@link
- // InlineSuggestionsRequest}, but the IME will only return it after 2) happens (or return
- // immediately if the IME doesn't support inline suggestions). Also there is IPC latency
- // between the framework and the IME but that should be small compare to that.
- private static final int CREATE_INLINE_SUGGESTIONS_REQUEST_TIMEOUT_MS = 1000;
-
@NonNull
private final InputMethodManagerInternal mInputMethodManagerInternal;
private final int mUserId;
@@ -92,9 +82,6 @@
@GuardedBy("mLock")
@Nullable
private IInlineSuggestionsResponseCallback mResponseCallback;
- @GuardedBy("mLock")
- @Nullable
- private Runnable mTimeoutCallback;
@GuardedBy("mLock")
@Nullable
@@ -174,12 +161,17 @@
}
/**
- * This method must be called when the session is destroyed, to avoid further callbacks from/to
- * the IME.
+ * Prevents further interaction with the IME. Must be called before starting a new request
+ * session to avoid unwanted behavior from two overlapping requests.
*/
@GuardedBy("mLock")
void destroySessionLocked() {
mDestroyed = true;
+
+ if (!mImeRequestReceived) {
+ Slog.w(TAG,
+ "Never received an InlineSuggestionsRequest from the IME for " + mAutofillId);
+ }
}
/**
@@ -196,11 +188,6 @@
mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId,
new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras),
new InlineSuggestionsRequestCallbackImpl(this));
- mTimeoutCallback = () -> {
- Slog.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
- handleOnReceiveImeRequest(null, null);
- };
- mHandler.postDelayed(mTimeoutCallback, CREATE_INLINE_SUGGESTIONS_REQUEST_TIMEOUT_MS);
}
/**
@@ -212,12 +199,12 @@
if (mDestroyed || mResponseCallback == null) {
return;
}
- if (!mImeInputViewStarted && mPreviousResponseIsNotEmpty) {
- // 1. if previous response is not empty, and IME just become invisible, then send
- // empty response to make sure existing responses don't stick around on the IME.
+ if (!mImeInputStarted && mPreviousResponseIsNotEmpty) {
+ // 1. if previous response is not empty, and IME is just disconnected from the view,
+ // then send empty response to make sure existing responses don't stick around.
// Although the inline suggestions should disappear when IME hides which removes them
- // from the view hierarchy, but we still send an empty response to be extra safe.
-
+ // from the view hierarchy, but we still send an empty response to indicate that the
+ // previous suggestions are invalid now.
if (sVerbose) Slog.v(TAG, "Send empty inline response");
updateResponseToImeUncheckLocked(new InlineSuggestionsResponse(Collections.EMPTY_LIST));
mPreviousResponseIsNotEmpty = false;
@@ -264,11 +251,6 @@
}
mImeRequestReceived = true;
- if (mTimeoutCallback != null) {
- if (sVerbose) Slog.v(TAG, "removing timeout callback");
- mHandler.removeCallbacks(mTimeoutCallback);
- mTimeoutCallback = null;
- }
if (request != null && callback != null) {
mImeRequest = request;
mResponseCallback = callback;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ef17d13..1d3ab9b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2581,7 +2581,6 @@
if (sVerbose) Slog.v(TAG, "Exiting view " + id);
mUi.hideFillUi(this);
hideAugmentedAutofillLocked(viewState);
- mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
mCurrentViewId = null;
}
break;
@@ -2655,6 +2654,9 @@
if (sVerbose) {
Slog.v(TAG, "ignoring autofilled change on id " + id);
}
+ // TODO(b/156099633): remove this once framework gets out of business of resending
+ // inline suggestions when IME visibility changes.
+ mInlineSessionController.hideInlineSuggestionsUiLocked(viewState.id);
viewState.resetState(ViewState.STATE_CHANGED);
return;
} else if ((viewState.id.equals(this.mCurrentViewId))
@@ -3348,7 +3350,9 @@
if (generateEvent) {
mService.logDatasetSelected(dataset.getId(), id, mClientState);
}
-
+ if (mCurrentViewId != null) {
+ mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
+ }
autoFillApp(dataset);
return;
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 089eeb2..c8485b7 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -57,7 +57,6 @@
@NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
final BiConsumer<Dataset, Integer> onClickFactory = (dataset, datasetIndex) -> {
- client.requestHideFillUi(autofillId);
client.authenticate(response.getRequestId(),
datasetIndex, response.getAuthentication(), response.getClientState(),
/* authenticateInline= */ true);
@@ -65,7 +64,8 @@
final Consumer<IntentSender> intentSenderConsumer = (intentSender) ->
client.startIntentSender(intentSender, new Intent());
InlinePresentation inlineAuthentication = response.getInlinePresentation();
- return createInlineAuthSuggestion(inlineAuthentication,
+ return createInlineAuthSuggestion(
+ mergedInlinePresentation(request, 0, inlineAuthentication),
remoteRenderService, onClickFactory, onErrorCallback, intentSenderConsumer,
request.getHostInputToken(), request.getHostDisplayId());
}
@@ -85,7 +85,6 @@
final Consumer<IntentSender> intentSenderConsumer = (intentSender) ->
client.startIntentSender(intentSender, new Intent());
final BiConsumer<Dataset, Integer> onClickFactory = (dataset, datasetIndex) -> {
- client.requestHideFillUi(autofillId);
client.fill(requestId, datasetIndex, dataset);
};
diff --git a/services/core/Android.bp b/services/core/Android.bp
index cf85b1d..a95a0c2 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -97,7 +97,7 @@
"android.hardware.power-V1.0-java",
"android.hardware.tv.cec-V1.0-java",
"android.hardware.vibrator-java",
- "android.net.ipsec.ike.stubs.module_libs_api",
+ "android.net.ipsec.ike.stubs.module_lib",
"app-compat-annotations",
"framework-tethering-stubs-module_libs_api",
"service-permission-stubs",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4db00e3..1197154 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2740,7 +2740,7 @@
// the Messenger, but if this ever changes, not making a defensive copy
// here will give attack vectors to clients using this code path.
networkCapabilities = new NetworkCapabilities(networkCapabilities);
- networkCapabilities.restrictCapabilitesForTestNetwork();
+ networkCapabilities.restrictCapabilitesForTestNetwork(nai.creatorUid);
}
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
break;
@@ -3087,10 +3087,6 @@
@Override
public void notifyDataStallSuspected(DataStallReportParcelable p) {
- final Message msg = mConnectivityDiagnosticsHandler.obtainMessage(
- ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED,
- p.detectionMethod, mNetId, p.timestampMillis);
-
final PersistableBundle extras = new PersistableBundle();
switch (p.detectionMethod) {
case DETECTION_METHOD_DNS_EVENTS:
@@ -3105,12 +3101,9 @@
log("Unknown data stall detection method, ignoring: " + p.detectionMethod);
return;
}
- msg.setData(new Bundle(extras));
- // NetworkStateTrackerHandler currently doesn't take any actions based on data
- // stalls so send the message directly to ConnectivityDiagnosticsHandler and avoid
- // the cost of going through two handlers.
- mConnectivityDiagnosticsHandler.sendMessage(msg);
+ proxyDataStallToConnectivityDiagnosticsHandler(
+ p.detectionMethod, mNetId, p.timestampMillis, extras);
}
@Override
@@ -3124,6 +3117,19 @@
}
}
+ private void proxyDataStallToConnectivityDiagnosticsHandler(int detectionMethod, int netId,
+ long timestampMillis, @NonNull PersistableBundle extras) {
+ final Message msg = mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED,
+ detectionMethod, netId, timestampMillis);
+ msg.setData(new Bundle(extras));
+
+ // NetworkStateTrackerHandler currently doesn't take any actions based on data
+ // stalls so send the message directly to ConnectivityDiagnosticsHandler and avoid
+ // the cost of going through two handlers.
+ mConnectivityDiagnosticsHandler.sendMessage(msg);
+ }
+
private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
return isPrivateDnsValidationRequired(nai.networkCapabilities);
}
@@ -5859,7 +5865,7 @@
// the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
// sees capabilities that may be malicious, which might prevent mistakes in the future.
networkCapabilities = new NetworkCapabilities(networkCapabilities);
- networkCapabilities.restrictCapabilitesForTestNetwork();
+ networkCapabilities.restrictCapabilitesForTestNetwork(Binder.getCallingUid());
} else {
enforceNetworkFactoryPermission();
}
@@ -5872,7 +5878,7 @@
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
- this, mNetd, mDnsResolver, mNMS, providerId);
+ this, mNetd, mDnsResolver, mNMS, providerId, Binder.getCallingUid());
// Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
@@ -8154,4 +8160,24 @@
0,
callback));
}
+
+ @Override
+ public void simulateDataStall(int detectionMethod, long timestampMillis,
+ @NonNull Network network, @NonNull PersistableBundle extras) {
+ enforceAnyPermissionOf(android.Manifest.permission.MANAGE_TEST_NETWORKS,
+ android.Manifest.permission.NETWORK_STACK);
+ final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
+ if (!nc.hasTransport(TRANSPORT_TEST)) {
+ throw new SecurityException("Data Stall simluation is only possible for test networks");
+ }
+
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai == null || nai.creatorUid != Binder.getCallingUid()) {
+ throw new SecurityException("Data Stall simulation is only possible for network "
+ + "creators");
+ }
+
+ proxyDataStallToConnectivityDiagnosticsHandler(
+ detectionMethod, network.netId, timestampMillis, extras);
+ }
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 43ed853..e2a0c29 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -121,6 +121,7 @@
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.provider.DeviceConfig;
+import android.provider.DocumentsContract;
import android.provider.Downloads;
import android.provider.MediaStore;
import android.provider.Settings;
@@ -432,6 +433,8 @@
private volatile int mDownloadsAuthorityAppId = -1;
+ private volatile int mExternalStorageAuthorityAppId = -1;
+
private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
private final Installer mInstaller;
@@ -1923,24 +1926,22 @@
mIAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
- ProviderInfo provider = mPmInternal.resolveContentProvider(
- MediaStore.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.getUserId(UserHandle.USER_SYSTEM));
+ ProviderInfo provider = getProviderInfo(MediaStore.AUTHORITY);
if (provider != null) {
mMediaStoreAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
sMediaStoreAuthorityProcessName = provider.applicationInfo.processName;
}
- provider = mPmInternal.resolveContentProvider(
- Downloads.Impl.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.getUserId(UserHandle.USER_SYSTEM));
-
+ provider = getProviderInfo(Downloads.Impl.AUTHORITY);
if (provider != null) {
mDownloadsAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
}
+ provider = getProviderInfo(DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY);
+ if (provider != null) {
+ mExternalStorageAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
+ }
+
if (!mIsFuseEnabled) {
try {
mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null,
@@ -1951,6 +1952,13 @@
}
}
+ private ProviderInfo getProviderInfo(String authority) {
+ return mPmInternal.resolveContentProvider(
+ authority, PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ UserHandle.getUserId(UserHandle.USER_SYSTEM));
+ }
+
private void updateLegacyStorageApps(String packageName, int uid, boolean hasLegacy) {
synchronized (mLock) {
if (hasLegacy) {
@@ -4191,9 +4199,11 @@
return Zygote.MOUNT_EXTERNAL_PASS_THROUGH;
}
- if (mIsFuseEnabled && mDownloadsAuthorityAppId == UserHandle.getAppId(uid)) {
+ if (mIsFuseEnabled && (mDownloadsAuthorityAppId == UserHandle.getAppId(uid)
+ || mExternalStorageAuthorityAppId == UserHandle.getAppId(uid))) {
// DownloadManager can write in app-private directories on behalf of apps;
// give it write access to Android/
+ // ExternalStorageProvider can access Android/{data,obb} dirs in managed mode
return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE;
}
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 0ea7346..d6bd5a1 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -317,39 +317,34 @@
"Cannot create network for non ipsec, non-testtun interface");
}
- // Setup needs to be done with NETWORK_STACK privileges.
- int callingUid = Binder.getCallingUid();
- Binder.withCleanCallingIdentity(
- () -> {
- try {
- mNMS.setInterfaceUp(iface);
+ try {
+ // This requires NETWORK_STACK privileges.
+ Binder.withCleanCallingIdentity(() -> mNMS.setInterfaceUp(iface));
- // Synchronize all accesses to mTestNetworkTracker to prevent the case
- // where:
- // 1. TestNetworkAgent successfully binds to death of binder
- // 2. Before it is added to the mTestNetworkTracker, binder dies,
- // binderDied() is called (on a different thread)
- // 3. This thread is pre-empted, put() is called after remove()
- synchronized (mTestNetworkTracker) {
- TestNetworkAgent agent =
- registerTestNetworkAgent(
- mHandler.getLooper(),
- mContext,
- iface,
- lp,
- isMetered,
- callingUid,
- administratorUids,
- binder);
+ // Synchronize all accesses to mTestNetworkTracker to prevent the case where:
+ // 1. TestNetworkAgent successfully binds to death of binder
+ // 2. Before it is added to the mTestNetworkTracker, binder dies, binderDied() is called
+ // (on a different thread)
+ // 3. This thread is pre-empted, put() is called after remove()
+ synchronized (mTestNetworkTracker) {
+ TestNetworkAgent agent =
+ registerTestNetworkAgent(
+ mHandler.getLooper(),
+ mContext,
+ iface,
+ lp,
+ isMetered,
+ Binder.getCallingUid(),
+ administratorUids,
+ binder);
- mTestNetworkTracker.put(agent.getNetwork().netId, agent);
- }
- } catch (SocketException e) {
- throw new UncheckedIOException(e);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- });
+ mTestNetworkTracker.put(agent.getNetwork().netId, agent);
+ }
+ } catch (SocketException e) {
+ throw new UncheckedIOException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** Teardown a test network */
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d914bda..930f124 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13597,7 +13597,9 @@
pw.print(" unmapped + ");
pw.print(stringifyKBSize(ionPool));
pw.println(" pools)");
- kernelUsed += ionUnmapped;
+ // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
+ // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
+ kernelUsed += ionHeap;
}
final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
@@ -14403,7 +14405,9 @@
memInfoBuilder.append(" ION: ");
memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
memInfoBuilder.append("\n");
- kernelUsed += ionUnmapped;
+ // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
+ // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
+ kernelUsed += ionHeap;
}
memInfoBuilder.append(" Used RAM: ");
memInfoBuilder.append(stringifyKBSize(
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index befd6b1..3ee3d50 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -286,6 +286,13 @@
final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile,
suppressNoisyIntent, a2dpVolume);
+ new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
+ + "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent")
+ .set(MediaMetrics.Property.STATE, state == BluetoothProfile.STATE_CONNECTED
+ ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
+ .set(MediaMetrics.Property.INDEX, a2dpVolume)
+ .record();
+
// operations of removing and posting messages related to A2DP device state change must be
// mutually exclusive
synchronized (mDeviceStateLock) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 17baead..8068e37 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7369,10 +7369,32 @@
return false;
}
boolean suppress = false;
- if (resolvedStream != AudioSystem.STREAM_MUSIC && mController != null) {
+ // Intended behavior:
+ // 1/ if the stream is not the default UI stream, do not suppress (as it is not involved
+ // in bringing up the UI)
+ // 2/ if the resolved and default stream is MUSIC, and media is playing, do not suppress
+ // 3/ otherwise suppress the first adjustments that occur during the "long press
+ // timeout" interval. Note this is true regardless of whether this is a "real long
+ // press" (where the user keeps pressing on the volume button), or repeated single
+ // presses (here we don't know if we are in a real long press, or repeated fast
+ // button presses).
+ // Once the long press timeout occurs (mNextLongPress reset to 0), do not suppress.
+ // Example: for a default and resolved stream of MUSIC, this allows modifying rapidly
+ // the volume when media is playing (whether by long press or repeated individual
+ // presses), or to bring up the volume UI when media is not playing, in order to make
+ // another change (e.g. switch ringer modes) without changing media volume.
+ if (resolvedStream == DEFAULT_VOL_STREAM_NO_PLAYBACK && mController != null) {
+ // never suppress media vol adjustement during media playback
+ if (resolvedStream == AudioSystem.STREAM_MUSIC
+ && AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, mLongPressTimeout))
+ {
+ // media is playing, adjust the volume right away
+ return false;
+ }
+
final long now = SystemClock.uptimeMillis();
if ((flags & AudioManager.FLAG_SHOW_UI) != 0 && !mVisible) {
- // ui will become visible
+ // UI is not visible yet, adjustment is ignored
if (mNextLongPress < now) {
mNextLongPress = now + mLongPressTimeout;
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 9e7b428..b6ffcef 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -358,14 +358,11 @@
* @return false if SCO isn't connected
*/
/*package*/ synchronized boolean isBluetoothScoOn() {
- if ((mBluetoothHeadset != null)
- && (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
- != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
- Log.w(TAG, "isBluetoothScoOn(true) returning false because "
- + mBluetoothHeadsetDevice + " is not in audio connected mode");
+ if (mBluetoothHeadset == null) {
return false;
}
- return true;
+ return mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
+ == BluetoothHeadset.STATE_AUDIO_CONNECTED;
}
/**
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 15628f0..37b2de1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -168,6 +168,9 @@
// Obtained by ConnectivityService and merged into NetworkAgent-provided information.
public CaptivePortalData captivePortalData;
+ // The UID of the remote entity that created this Network.
+ public final int creatorUid;
+
// Networks are lingered when they become unneeded as a result of their NetworkRequests being
// satisfied by a higher-scoring network. so as to allow communication to wrap up before the
// network is taken down. This usually only happens to the default network. Lingering ends with
@@ -268,7 +271,8 @@
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
- IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) {
+ IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
+ int creatorUid) {
this.messenger = messenger;
asyncChannel = ac;
network = net;
@@ -282,6 +286,7 @@
mHandler = handler;
networkAgentConfig = config;
this.factorySerialNumber = factorySerialNumber;
+ this.creatorUid = creatorUid;
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 5541b11..1810963 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -44,9 +44,6 @@
public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
private static final String TAG = "HdmiCecLocalDevicePlayback";
- private static final boolean WAKE_ON_HOTPLUG =
- SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, false);
-
private static final boolean SET_MENU_LANGUAGE =
HdmiProperties.set_menu_language().orElse(false);
@@ -152,9 +149,6 @@
assertRunOnServiceThread();
mCecMessageCache.flushAll();
// We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3.
- if (WAKE_ON_HOTPLUG && connected && mService.isPowerStandbyOrTransient()) {
- mService.wakeUp();
- }
if (!connected) {
getWakeLock().release();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 1ed5cd8..549e336 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2207,7 +2207,9 @@
@Override
public void setHdmiCecVolumeControlEnabled(final boolean isHdmiCecVolumeControlEnabled) {
enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
HdmiControlService.this.setHdmiCecVolumeControlEnabled(isHdmiCecVolumeControlEnabled);
+ Binder.restoreCallingIdentity(token);
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index d49d4b2..de13bd8 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -45,6 +45,12 @@
}
/**
+ * Called by the power manager to tell the input method manager whether it
+ * should start watching for wake events.
+ */
+ public abstract void setInteractive(boolean interactive);
+
+ /**
* Hides the current input method, if visible.
*/
public abstract void hideCurrentInputMethod(@SoftInputShowHideReason int reason);
@@ -108,6 +114,10 @@
private static final InputMethodManagerInternal NOP =
new InputMethodManagerInternal() {
@Override
+ public void setInteractive(boolean interactive) {
+ }
+
+ @Override
public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b949d6b..93ef1264 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4202,6 +4202,9 @@
+ ((ClientState)msg.obj).uid);
}
return true;
+ case MSG_SET_INTERACTIVE:
+ handleSetInteractive(msg.arg1 != 0);
+ return true;
case MSG_REPORT_FULLSCREEN_MODE: {
final boolean fullscreen = msg.arg1 != 0;
final ClientState clientState = (ClientState)msg.obj;
@@ -4259,7 +4262,7 @@
}
// ---------------------------------------------------------------
- case MSG_INLINE_SUGGESTIONS_REQUEST:
+ case MSG_INLINE_SUGGESTIONS_REQUEST: {
args = (SomeArgs) msg.obj;
final InlineSuggestionsRequestInfo requestInfo =
(InlineSuggestionsRequestInfo) args.arg2;
@@ -4271,11 +4274,27 @@
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
}
+ args.recycle();
return true;
+ }
}
return false;
}
+ private void handleSetInteractive(final boolean interactive) {
+ synchronized (mMethodMap) {
+ mIsInteractive = interactive;
+ updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);
+
+ // Inform the current client of the change in active status
+ if (mCurClient != null && mCurClient.client != null) {
+ executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
+ MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mInFullscreenMode ? 1 : 0,
+ mCurClient));
+ }
+ }
+ }
+
private boolean chooseNewDefaultIMELocked() {
final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
mSettings.getEnabledInputMethodListLocked());
@@ -4885,6 +4904,13 @@
}
@Override
+ public void setInteractive(boolean interactive) {
+ // Do everything in handler so as not to block the caller.
+ mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
+ .sendToTarget();
+ }
+
+ @Override
public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget();
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 0b73e4f..2129e9b 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -171,6 +171,11 @@
LocalServices.addService(InputMethodManagerInternal.class,
new InputMethodManagerInternal() {
@Override
+ public void setInteractive(boolean interactive) {
+ reportNotSupported();
+ }
+
+ @Override
public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
reportNotSupported();
}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 905a10b..817902d 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -190,6 +190,9 @@
public void updateRuleSet(
String version, ParceledListSlice<Rule> rules, IntentSender statusReceiver) {
String ruleProvider = getCallerPackageNameOrThrow(Binder.getCallingUid());
+ if (DEBUG_INTEGRITY_COMPONENT) {
+ Slog.i(TAG, String.format("Calling rule provider name is: %s.", ruleProvider));
+ }
mHandler.post(
() -> {
@@ -201,6 +204,9 @@
success = false;
}
+ if (DEBUG_INTEGRITY_COMPONENT) {
+ Slog.i(TAG, String.format("Successfully pushed rule set: %s", version));
+ }
FrameworkStatsLog.write(
FrameworkStatsLog.INTEGRITY_RULES_PUSHED,
success,
@@ -673,9 +679,6 @@
// Identify the package names in the caller list.
List<String> callingPackageNames = getPackageListForUid(callingUid);
- if (DEBUG_INTEGRITY_COMPONENT) {
- Slog.i(TAG, String.format("Calling packages are: ", callingPackageNames));
- }
// Find the intersection between the allowed and calling packages. Ideally, we will have
// at most one package name here. But if we have more, it is fine.
@@ -685,10 +688,7 @@
allowedCallingPackages.add(packageName);
}
}
- if (DEBUG_INTEGRITY_COMPONENT) {
- Slog.i(TAG,
- String.format("Calling rule pusher packages are: ", allowedCallingPackages));
- }
+
return allowedCallingPackages.isEmpty() ? null : allowedCallingPackages.get(0);
}
@@ -706,9 +706,6 @@
Arrays.asList(
mContext.getResources()
.getStringArray(R.array.config_integrityRuleProviderPackages));
- if (DEBUG_INTEGRITY_COMPONENT) {
- Slog.i(TAG, String.format("Rule provider list contains: %s", integrityRuleProviders));
- }
// Filter out the rule provider packages that are not system apps.
List<String> systemAppRuleProviders = new ArrayList<>();
diff --git a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
index 75fd7dc..927fcfb 100644
--- a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
@@ -630,7 +630,7 @@
}
Notification.Builder builder = new Notification.Builder(mContext,
- SystemNotificationChannels.NETWORK_ALERTS)
+ SystemNotificationChannels.NETWORK_STATUS)
.setSmallIcon(R.drawable.stat_sys_gps_on)
.setCategory(Notification.CATEGORY_SYSTEM)
.setVisibility(Notification.VISIBILITY_SECRET)
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 54958d3..fd8e03e 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -28,10 +28,12 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
+import android.media.AudioSystem;
import android.media.MediaRoute2Info;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import com.android.internal.R;
@@ -55,6 +57,9 @@
@SuppressWarnings("WeakerAccess") /* synthetic access */
BluetoothHearingAid mHearingAidProfile;
+ // Route type -> volume map
+ private final SparseIntArray mVolumeMap = new SparseIntArray();
+
private final Context mContext;
private final BluetoothAdapter mBluetoothAdapter;
private final BluetoothRoutesUpdatedListener mListener;
@@ -192,11 +197,30 @@
return routes;
}
- boolean setSelectedRouteVolume(int volume) {
- if (mSelectedRoute == null) return false;
+ /**
+ * Updates the volume for {@link AudioManager#getDevicesForStream(int) devices}.
+ *
+ * @return true if devices can be handled by the provider.
+ */
+ public boolean updateVolumeForDevices(int devices, int volume) {
+ int routeType;
+ if ((devices & (AudioSystem.DEVICE_OUT_HEARING_AID)) != 0) {
+ routeType = MediaRoute2Info.TYPE_HEARING_AID;
+ } else if ((devices & (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP
+ | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
+ | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
+ routeType = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
+ } else {
+ return false;
+ }
+ mVolumeMap.put(routeType, volume);
+ if (mSelectedRoute == null || mSelectedRoute.route.getType() != routeType) {
+ return true;
+ }
mSelectedRoute.route = new MediaRoute2Info.Builder(mSelectedRoute.route)
.setVolume(volume)
.build();
+ notifyBluetoothRoutesUpdated();
return true;
}
@@ -222,6 +246,7 @@
R.string.bluetooth_a2dp_audio_route_name).toString())
.setType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)
.setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
.build();
newBtRoute.connectedProfiles = new SparseBooleanArray();
return newBtRoute;
@@ -240,13 +265,10 @@
// Update volume when the connection state is changed.
MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(btRoute.route)
.setConnectionState(state);
- builder.setType(btRoute.connectedProfiles.get(BluetoothProfile.HEARING_AID, false)
- ? MediaRoute2Info.TYPE_HEARING_AID : MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
+ builder.setType(btRoute.getRouteType());
if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
- int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
- builder.setVolumeMax(maxVolume).setVolume(currentVolume);
+ builder.setVolume(mVolumeMap.get(btRoute.getRouteType(), 0));
}
btRoute.route = builder.build();
}
@@ -259,6 +281,15 @@
public BluetoothDevice btDevice;
public MediaRoute2Info route;
public SparseBooleanArray connectedProfiles;
+
+ @MediaRoute2Info.Type
+ int getRouteType() {
+ // Let hearing aid profile have a priority.
+ if (connectedProfiles.get(BluetoothProfile.HEARING_AID, false)) {
+ return MediaRoute2Info.TYPE_HEARING_AID;
+ }
+ return MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
+ }
}
// These callbacks run on the main thread.
@@ -285,13 +316,12 @@
btRoute = createBluetoothRoute(device);
mBluetoothRoutes.put(device.getAddress(), btRoute);
}
+ btRoute.connectedProfiles.put(profile, true);
if (activeDevices.contains(device)) {
mSelectedRoute = btRoute;
setRouteConnectionState(mSelectedRoute,
MediaRoute2Info.CONNECTION_STATE_CONNECTED);
}
-
- btRoute.connectedProfiles.put(profile, true);
}
notifyBluetoothRoutesUpdated();
}
@@ -348,6 +378,8 @@
BluetoothDevice.ERROR);
BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
if (bondState == BluetoothDevice.BOND_BONDED && btRoute == null) {
+ //TODO: The type of the new route is A2DP even when it's HEARING_AID.
+ // We may determine the type of route when create the route.
btRoute = createBluetoothRoute(device);
if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) {
btRoute.connectedProfiles.put(BluetoothProfile.A2DP, true);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 5e865e7..d7bd794 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -584,7 +584,8 @@
mAllRouterRecords.put(binder, routerRecord);
userRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::notifyRoutesToRouter, userRecord.mHandler, router));
+ obtainMessage(UserHandler::notifyRoutesToRouter,
+ userRecord.mHandler, routerRecord));
}
private void unregisterRouter2Locked(@NonNull IMediaRouter2 router, boolean died) {
@@ -1775,16 +1776,36 @@
}
}
- private void notifyRoutesToRouter(@NonNull IMediaRouter2 router) {
+ private void notifyRoutesToRouter(@NonNull RouterRecord routerRecord) {
List<MediaRoute2Info> routes = new ArrayList<>();
+
+ MediaRoute2ProviderInfo systemProviderInfo = null;
for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) {
+ // TODO: Create MediaRoute2ProviderInfo#isSystemProvider()
+ if (TextUtils.equals(providerInfo.getUniqueId(), mSystemProvider.getUniqueId())) {
+ // Adding routes from system provider will be handled below, so skip it here.
+ systemProviderInfo = providerInfo;
+ continue;
+ }
routes.addAll(providerInfo.getRoutes());
}
+
+ if (routerRecord.mHasModifyAudioRoutingPermission) {
+ if (systemProviderInfo != null) {
+ routes.addAll(systemProviderInfo.getRoutes());
+ } else {
+ // This shouldn't happen.
+ Slog.w(TAG, "notifyRoutesToRouter: System route provider not found.");
+ }
+ } else {
+ routes.add(mSystemProvider.getDefaultRoute());
+ }
+
if (routes.size() == 0) {
return;
}
try {
- router.notifyRoutesAdded(routes);
+ routerRecord.mRouter.notifyRoutesAdded(routes);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify all routes. Router probably died.", ex);
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index b585b49..fdee9f8 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -81,6 +81,7 @@
MediaRoute2Info mDeviceRoute;
RoutingSessionInfo mDefaultSessionInfo;
final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
+ int mDeviceVolume;
private final Object mRequestLock = new Object();
@GuardedBy("mRequestLock")
@@ -127,8 +128,9 @@
});
updateSessionInfosIfNeeded();
- mContext.registerReceiver(new VolumeChangeReceiver(),
- new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
+ IntentFilter intentFilter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
+ intentFilter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
+ mContext.registerReceiver(new AudioManagerBroadcastReceiver(), intentFilter);
if (mBtRouteProvider != null) {
mHandler.post(() -> {
@@ -136,6 +138,7 @@
notifyProviderState();
});
}
+ updateVolume();
}
@Override
@@ -248,8 +251,8 @@
.setVolumeHandling(mAudioManager.isVolumeFixed()
? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
: MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolume(mDeviceVolume)
.setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
- .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
.setType(type)
.addFeature(FEATURE_LIVE_AUDIO)
.addFeature(FEATURE_LIVE_VIDEO)
@@ -361,36 +364,43 @@
}
}
- private class VolumeChangeReceiver extends BroadcastReceiver {
+ void updateVolume() {
+ int devices = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
+ int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+
+ if (mDefaultRoute.getVolume() != volume) {
+ mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute)
+ .setVolume(volume)
+ .build();
+ }
+
+ if (mBtRouteProvider != null && mBtRouteProvider.updateVolumeForDevices(devices, volume)) {
+ return;
+ }
+ if (mDeviceVolume != volume) {
+ mDeviceVolume = volume;
+ mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
+ .setVolume(volume)
+ .build();
+ }
+ publishProviderState();
+ }
+
+ private class AudioManagerBroadcastReceiver extends BroadcastReceiver {
// This will be called in the main thread.
@Override
public void onReceive(Context context, Intent intent) {
- if (!intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
+ if (!intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)
+ && !intent.getAction().equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
return;
}
- final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
if (streamType != AudioManager.STREAM_MUSIC) {
return;
}
- final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
- final int oldVolume = intent.getIntExtra(
- AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
-
- if (newVolume != oldVolume) {
- if (TextUtils.equals(mDeviceRoute.getId(), mSelectedRouteId)) {
- mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
- .setVolume(newVolume)
- .build();
- } else if (mBtRouteProvider != null) {
- mBtRouteProvider.setSelectedRouteVolume(newVolume);
- }
- mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute)
- .setVolume(newVolume)
- .build();
- publishProviderState();
- }
+ updateVolume();
}
}
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 28c8642d..e82ee22 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -50,6 +50,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.PermissionManager;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.Slog;
@@ -458,6 +459,10 @@
+ packageName + " on user ID " + userId);
return;
}
+
+ final boolean hadPermission = hasInteractAcrossProfilesPermission(
+ packageName, uid, PermissionChecker.PID_UNKNOWN);
+
final int callingUid = mInjector.getCallingUid();
if (isPermissionGranted(
Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) {
@@ -472,6 +477,22 @@
}
sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId));
maybeLogSetInteractAcrossProfilesAppOp(packageName, newMode, userId, logMetrics, uid);
+ maybeKillUid(packageName, uid, hadPermission);
+ }
+
+ /**
+ * Kills the process represented by the given UID if it has lost the permission to
+ * interact across profiles.
+ */
+ private void maybeKillUid(
+ String packageName, int uid, boolean hadPermission) {
+ if (!hadPermission) {
+ return;
+ }
+ if (hasInteractAcrossProfilesPermission(packageName, uid, PermissionChecker.PID_UNKNOWN)) {
+ return;
+ }
+ mInjector.killUid(packageName, uid);
}
private void maybeLogSetInteractAcrossProfilesAppOp(
@@ -774,6 +795,18 @@
String permission, int uid, int owningUid, boolean exported) {
return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported);
}
+
+ @Override
+ public void killUid(String packageName, int uid) {
+ try {
+ ActivityManager.getService().killApplication(
+ packageName,
+ UserHandle.getAppId(uid),
+ UserHandle.getUserId(uid),
+ PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED);
+ } catch (RemoteException ignored) {
+ }
+ }
}
@VisibleForTesting
@@ -813,6 +846,8 @@
void sendBroadcastAsUser(Intent intent, UserHandle user);
int checkComponentPermission(String permission, int uid, int owningUid, boolean exported);
+
+ void killUid(String packageName, int uid);
}
class LocalService extends CrossProfileAppsInternal {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7af0787..1741aa7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -395,7 +395,6 @@
private boolean mDataLoaderFinished = false;
- // TODO(b/146080380): merge file list with Callback installation.
private IncrementalFileStorages mIncrementalFileStorages;
private static final FileFilter sAddedApkFilter = new FileFilter() {
@@ -2698,6 +2697,7 @@
/**
* Makes sure files are present in staging location.
+ * @return if the image is ready for installation
*/
@GuardedBy("mLock")
private boolean prepareDataLoaderLocked()
@@ -2709,6 +2709,17 @@
return true;
}
+ // Retrying commit.
+ if (mIncrementalFileStorages != null) {
+ try {
+ mIncrementalFileStorages.startLoading();
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
+ e.getCause());
+ }
+ return false;
+ }
+
final List<InstallationFileParcel> addedFiles = new ArrayList<>();
final List<String> removedFiles = new ArrayList<>();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3ead72c..7959461 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -608,6 +608,12 @@
private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000;
/**
+ * The default maximum time to wait for the integrity verification to return in
+ * milliseconds.
+ */
+ private static final long DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT = 30 * 1000;
+
+ /**
* Timeout duration in milliseconds for enabling package rollback. If we fail to enable
* rollback within that period, the install will proceed without rollback enabled.
*
@@ -13838,6 +13844,19 @@
}
/**
+ * Get the integrity verification timeout.
+ *
+ * @return verification timeout in milliseconds
+ */
+ private long getIntegrityVerificationTimeout() {
+ long timeout = Global.getLong(mContext.getContentResolver(),
+ Global.APP_INTEGRITY_VERIFICATION_TIMEOUT, DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
+ // The setting can be used to increase the timeout but not decrease it, since that is
+ // equivalent to disabling the integrity component.
+ return Math.max(timeout, DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
+ }
+
+ /**
* Get the default verification agent response code.
*
* @return default verification response code
@@ -15032,8 +15051,7 @@
final Message msg =
mHandler.obtainMessage(CHECK_PENDING_INTEGRITY_VERIFICATION);
msg.arg1 = verificationId;
- // TODO: do we want to use the same timeout?
- mHandler.sendMessageDelayed(msg, getVerificationTimeout());
+ mHandler.sendMessageDelayed(msg, getIntegrityVerificationTimeout());
}
}, /* scheduler= */ null,
/* initialCode= */ 0,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index bc94528..0dc4d13 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3172,7 +3172,7 @@
metadata = (streamingVersion == 0) ? Metadata.forDataOnlyStreaming(fileId)
: Metadata.forStreaming(fileId);
try {
- if (V4Signature.readFrom(signature) == null) {
+ if ((signature.length > 0) && (V4Signature.readFrom(signature) == null)) {
getErrPrintWriter().println("V4 signature is invalid in: " + arg);
return 1;
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index b805a24..79805e3 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -69,6 +69,8 @@
import com.android.internal.content.PackageHelper;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -110,6 +112,9 @@
private final List<String> mFailedPackageNames = new ArrayList<>();
private String mNativeFailureReason;
+ @GuardedBy("mSuccessfulStagedSessionIds")
+ private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
+
StagingManager(PackageInstallerService pi, Context context,
Supplier<PackageParser2> packageParserSupplier) {
mPi = pi;
@@ -122,6 +127,34 @@
BackgroundThread.get().getLooper());
}
+ /**
+ This class manages lifecycle events for StagingManager.
+ */
+ public static final class Lifecycle extends SystemService {
+ private static StagingManager sStagingManager;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ void startService(StagingManager stagingManager) {
+ sStagingManager = stagingManager;
+ LocalServices.getService(SystemServiceManager.class).startService(this);
+ }
+
+ @Override
+ public void onStart() {
+ // no-op
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_BOOT_COMPLETED && sStagingManager != null) {
+ sStagingManager.markStagedSessionsAsSuccessful();
+ }
+ }
+ }
+
private void updateStoredSession(@NonNull PackageInstallerSession sessionInfo) {
synchronized (mStagedSessions) {
PackageInstallerSession storedSession = mStagedSessions.get(sessionInfo.sessionId);
@@ -652,7 +685,22 @@
Slog.d(TAG, "Marking session " + session.sessionId + " as applied");
session.setStagedSessionApplied();
if (hasApex) {
- mApexManager.markStagedSessionSuccessful(session.sessionId);
+ try {
+ if (supportsCheckpoint()) {
+ // Store the session ID, which will be marked as successful by ApexManager
+ // upon boot completion.
+ synchronized (mSuccessfulStagedSessionIds) {
+ mSuccessfulStagedSessionIds.add(session.sessionId);
+ }
+ } else {
+ // Mark sessions as successful immediately on non-checkpointing devices.
+ mApexManager.markStagedSessionSuccessful(session.sessionId);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Checkpoint support unknown, marking session as successful "
+ + "immediately.");
+ mApexManager.markStagedSessionSuccessful(session.sessionId);
+ }
}
}
@@ -1121,7 +1169,16 @@
}
}
+ void markStagedSessionsAsSuccessful() {
+ synchronized (mSuccessfulStagedSessionIds) {
+ for (int i = 0; i < mSuccessfulStagedSessionIds.size(); i++) {
+ mApexManager.markStagedSessionSuccessful(mSuccessfulStagedSessionIds.get(i));
+ }
+ }
+ }
+
void systemReady() {
+ new Lifecycle(mContext).startService(this);
// Register the receiver of boot completed intent for staging manager.
mContext.registerReceiver(new BroadcastReceiver() {
@Override
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index f8d039c..530c115 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -344,7 +344,7 @@
*/
@NonNull
private List<String> getPackagesWhitelistErrors(@PackageWhitelistMode int mode) {
- if ((!isEnforceMode(mode) || isImplicitWhitelistMode(mode)) && !isLogMode(mode)) {
+ if ((!isEnforceMode(mode) || isImplicitWhitelistMode(mode))) {
return Collections.emptyList();
}
@@ -752,6 +752,10 @@
} else if (mode == USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT) {
mode = getDeviceDefaultWhitelistMode();
}
+ if (criticalOnly) {
+ // Flip-out log mode
+ mode &= ~USER_TYPE_PACKAGE_WHITELIST_MODE_LOG;
+ }
Slog.v(TAG, "dumpPackageWhitelistProblems(): using mode " + modeToString(mode));
final List<String> errors = getPackagesWhitelistErrors(mode);
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 8648a23..76c6a7a 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -25,6 +25,8 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ALL;
@@ -45,7 +47,6 @@
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PermissionInfo;
import android.os.Build;
-import android.os.Handler;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -57,7 +58,6 @@
import android.telecom.TelecomManager;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Log;
import android.util.LongSparseLongArray;
import android.util.Pair;
import android.util.Slog;
@@ -93,6 +93,7 @@
public final class PermissionPolicyService extends SystemService {
private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName();
private static final boolean DEBUG = false;
+ private static final long USER_SENSITIVE_UPDATE_DELAY_MS = 10000;
private final Object mLock = new Object();
@@ -378,8 +379,6 @@
* TODO ntmyren: Remove once propagated, and state is repaired
*/
private void restoreReadPhoneStatePermissions(int userId) {
- PermissionControllerManager manager = new PermissionControllerManager(this.getContext(),
- Handler.getMain());
PackageManager pm = getContext().getPackageManager();
List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(
MATCH_ALL | GET_PERMISSIONS, userId);
@@ -389,26 +388,21 @@
continue;
}
- boolean hasReadPhoneState = false;
+ UserHandle user = UserHandle.getUserHandleForUid(pI.applicationInfo.uid);
for (int j = pI.requestedPermissions.length - 1; j >= 0; j--) {
if (pI.requestedPermissions[j].equals(READ_PHONE_STATE)) {
- hasReadPhoneState = true;
+ int flags = pm.getPermissionFlags(READ_PHONE_STATE, pI.packageName, user);
+ // If the app is auto revoked for read phone state, and is only user sensitive
+ // when granted, clear auto revoked flag.
+ if ((flags & FLAG_PERMISSION_AUTO_REVOKED) != 0
+ && (flags & FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0
+ && (flags & FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) == 0) {
+ pm.updatePermissionFlags(READ_PHONE_STATE, pI.packageName,
+ FLAG_PERMISSION_AUTO_REVOKED, 0, user);
+ }
+ break;
}
}
- if (!hasReadPhoneState) {
- continue;
- }
-
- Log.i(LOG_TAG, "Updating read phone state for " + pI.packageName + " "
- + pI.applicationInfo.uid);
- manager.updateUserSensitiveForApp(pI.applicationInfo.uid);
-
- UserHandle user = UserHandle.getUserHandleForUid(pI.applicationInfo.uid);
- int permFlags = pm.getPermissionFlags(READ_PHONE_STATE, pI.packageName, user);
- if ((permFlags & FLAG_PERMISSION_AUTO_REVOKED) != 0) {
- pm.updatePermissionFlags(READ_PHONE_STATE, pI.packageName,
- FLAG_PERMISSION_AUTO_REVOKED, 0, user);
- }
}
}
@@ -460,7 +454,8 @@
throw new IllegalStateException(e);
}
- permissionControllerManager.updateUserSensitive();
+ FgThread.getHandler().postDelayed(permissionControllerManager::updateUserSensitive,
+ USER_SENSITIVE_UPDATE_DELAY_MS);
packageManagerInternal.updateRuntimePermissionsFingerprint(userId);
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 199cb49..0b95be1 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -413,6 +413,7 @@
// Start input as soon as we start waking up or going to sleep.
mInputManagerInternal.setInteractive(interactive);
+ mInputMethodManagerInternal.setInteractive(interactive);
// Notify battery stats.
try {
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 9b0feae..12309f4 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -445,6 +445,8 @@
return pullAttributedAppOps(atomTag, data);
case FrameworkStatsLog.SETTING_SNAPSHOT:
return pullSettingsStats(atomTag, data);
+ case FrameworkStatsLog.DISPLAY_WAKE_REASON:
+ return pullDisplayWakeStats(atomTag, data);
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
@@ -609,6 +611,7 @@
registerBatteryVoltage();
registerBatteryCycleCount();
registerSettingsStats();
+ registerDisplayWakeStats();
}
private void initAndRegisterNetworkStatsPullers() {
@@ -3391,6 +3394,21 @@
return StatsManager.PULL_SUCCESS;
}
+ private void registerDisplayWakeStats() {
+ int tagId = FrameworkStatsLog.DISPLAY_WAKE_REASON;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
+ );
+ }
+
+ int pullDisplayWakeStats(int atomTag, List<StatsEvent> pulledData) {
+ //TODO: Denny, implement read/write DisplayWakeStats, b/154172964
+ return 0;
+ }
+
// Thermal event received from vendor thermal management subsystem
private static final class ThermalEventListener extends IThermalEventListener.Stub {
@Override
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 63952b0..31fbaff 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2743,7 +2743,7 @@
mContext, 0,
Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
- 0, null, new UserHandle(serviceUserId)));
+ PendingIntent.FLAG_IMMUTABLE, null, new UserHandle(serviceUserId)));
if (!mContext.bindServiceAsUser(intent, newConn,
Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 785ca90..34998a0 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -50,7 +50,7 @@
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Display;
-import android.view.InsetsState;
+import android.view.InsetsSource;
import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
@@ -80,6 +80,7 @@
private final WindowManagerService mService;
+ private static final Rect EMPTY_RECT = new Rect();
private static final float[] sTempFloats = new float[9];
public AccessibilityController(WindowManagerService service) {
@@ -1166,9 +1167,9 @@
}
static Rect getNavBarInsets(DisplayContent displayContent) {
- final InsetsState insetsState =
- displayContent.getInsetsStateController().getRawInsetsState();
- return insetsState.getSource(ITYPE_NAVIGATION_BAR).getFrame();
+ final InsetsSource source = displayContent.getInsetsStateController().getRawInsetsState()
+ .peekSource(ITYPE_NAVIGATION_BAR);
+ return source != null ? source.getFrame() : EMPTY_RECT;
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 131e449..e675afc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1705,7 +1705,7 @@
boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+ boolean allowTaskSnapshot, boolean activityCreated) {
// If the display is frozen, we won't do anything until the actual window is
// displayed so there is no reason to put in the starting window.
if (!okToDisplay()) {
@@ -1726,7 +1726,7 @@
mWmService.mTaskSnapshotController.getSnapshot(task.mTaskId, task.mUserId,
false /* restoreFromDisk */, false /* isLowResolution */);
final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
- allowTaskSnapshot, activityCreated, fromRecents, snapshot);
+ allowTaskSnapshot, activityCreated, snapshot);
if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
if (isActivityTypeHome()) {
@@ -1888,12 +1888,12 @@
private final AddStartingWindow mAddStartingWindow = new AddStartingWindow();
private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
+ boolean allowTaskSnapshot, boolean activityCreated,
ActivityManager.TaskSnapshot snapshot) {
if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else if (taskSwitch && allowTaskSnapshot) {
- if (snapshotOrientationSameAsTask(snapshot) || (snapshot != null && fromRecents)) {
+ if (isSnapshotCompatible(snapshot)) {
return STARTING_WINDOW_TYPE_SNAPSHOT;
}
if (!isActivityTypeHome()) {
@@ -1905,11 +1905,22 @@
}
}
- private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) {
+ /**
+ * Returns {@code true} if the task snapshot is compatible with this activity (at least the
+ * rotation must be the same).
+ */
+ @VisibleForTesting
+ boolean isSnapshotCompatible(ActivityManager.TaskSnapshot snapshot) {
if (snapshot == null) {
return false;
}
- return task.getConfiguration().orientation == snapshot.getOrientation();
+ final int rotation = mDisplayContent.rotationForActivityInDifferentOrientation(this);
+ final int targetRotation = rotation != ROTATION_UNDEFINED
+ // The display may rotate according to the orientation of this activity.
+ ? rotation
+ // The activity won't change display orientation.
+ : task.getWindowConfiguration().getRotation();
+ return snapshot.getRotation() == targetRotation;
}
void removeStartingWindow() {
@@ -1929,7 +1940,6 @@
mStartingData = null;
startingSurface = null;
startingWindow = null;
- startingDisplayed = false;
if (surface == null) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
"startingWindow was set but startingSurface==null, couldn't "
@@ -2184,8 +2194,10 @@
@Override
boolean isFocusable() {
- return super.isFocusable()
- && (getWindowConfiguration().canReceiveKeys() || isAlwaysFocusable());
+ // TODO(156521483): Propagate the state down the hierarchy instead of checking the parent
+ boolean canReceiveKeys = getWindowConfiguration().canReceiveKeys()
+ && getTask().getWindowConfiguration().canReceiveKeys();
+ return super.isFocusable() && (canReceiveKeys || isAlwaysFocusable());
}
boolean isResizeable() {
@@ -4785,16 +4797,7 @@
if (!task.hasChild(this)) {
throw new IllegalStateException("Activity not found in its task");
}
- final ActivityRecord activityAbove = task.getActivityAbove(this);
- if (activityAbove == null) {
- // It's the topmost activity in the task - should become resumed now
- return true;
- }
- // Check if activity above is finishing now and this one becomes the topmost in task.
- if (activityAbove.finishing) {
- return true;
- }
- return false;
+ return task.topRunningActivity() == this;
}
void handleAlreadyVisible() {
@@ -5453,7 +5456,6 @@
if (mLastTransactionSequence != mWmService.mTransactionSequence) {
mLastTransactionSequence = mWmService.mTransactionSequence;
mNumDrawnWindows = 0;
- startingDisplayed = false;
// There is the main base application window, even if it is exiting, wait for it
mNumInterestingWindows = findMainWindow(false /* includeStartingApp */) != null ? 1 : 0;
@@ -5673,11 +5675,6 @@
}
void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
- showStartingWindow(prev, newTask, taskSwitch, false /* fromRecents */);
- }
-
- void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
- boolean fromRecents) {
if (mTaskOverlay) {
// We don't show starting window for overlay activities.
return;
@@ -5694,8 +5691,7 @@
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
allowTaskSnapshot(),
- mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
- fromRecents);
+ mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal());
if (shown) {
mStartingWindowState = STARTING_WINDOW_SHOWN;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index dca0860..cb2d98c 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1307,17 +1307,8 @@
/**
* Make sure that all activities that need to be visible in the stack (that is, they
* currently can be seen by the user) actually are and update their configuration.
- * @param starting The top most activity in the task.
- * The activity is either starting or resuming.
- * Caller should ensure starting activity is visible.
- * @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link mEnsureActivitiesVisibleHelper}.
- * @param configChanges Parts of the configuration that changed for this activity for evaluating
- * if the screen should be frozen as part of
- * {@link mEnsureActivitiesVisibleHelper}.
- *
*/
- void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
+ void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows) {
ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
}
@@ -1326,19 +1317,9 @@
* Ensure visibility with an option to also update the configuration of visible activities.
* @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
* @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
- * @param starting The top most activity in the task.
- * The activity is either starting or resuming.
- * Caller should ensure starting activity is visible.
- * @param notifyClients Flag indicating whether the visibility updates should be sent to the
- * clients in {@link mEnsureActivitiesVisibleHelper}.
- * @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link mEnsureActivitiesVisibleHelper}.
- * @param configChanges Parts of the configuration that changed for this activity for evaluating
- * if the screen should be frozen as part of
- * {@link mEnsureActivitiesVisibleHelper}.
*/
// TODO: Should be re-worked based on the fact that each task as a stack in most cases.
- void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
+ void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
mTopActivityOccludesKeyguard = false;
mTopDismissingKeyguardActivity = null;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 3e7e0c8..62979ff 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2498,7 +2498,7 @@
mActivityMetricsLogger.notifyActivityLaunching(task.intent);
try {
mService.moveTaskToFrontLocked(null /* appThread */, null /* callingPackage */,
- task.mTaskId, 0, options, true /* fromRecents */);
+ task.mTaskId, 0, options);
// Apply options to prevent pendingOptions be taken by client to make sure
// the override pending app transition will be applied immediately.
targetActivity.applyOptionsLocked();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index bcdd6e3..79e8ee3 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1539,10 +1539,7 @@
*
* Note: This method should only be called from {@link #startActivityUnchecked}.
*/
-
- // TODO(b/152429287): Make it easier to exercise code paths through startActivityInner
- @VisibleForTesting
- int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
+ private int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity) {
@@ -1663,10 +1660,7 @@
// Also, we don't want to resume activities in a task that currently has an overlay
// as the starting activity just needs to be in the visible paused state until the
// over is removed.
- // Passing {@code null} as the start parameter ensures all activities are made
- // visible.
- mTargetStack.ensureActivitiesVisible(null /* starting */,
- 0 /* configChanges */, !PRESERVE_WINDOWS);
+ mTargetStack.ensureActivitiesVisible(mStartActivity, 0, !PRESERVE_WINDOWS);
// Go ahead and tell window manager to execute app transition for this activity
// since the app transition will not be triggered through the resume channel.
mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f21ec6b..78e4237 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2476,13 +2476,12 @@
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
synchronized (mGlobalLock) {
moveTaskToFrontLocked(appThread, callingPackage, taskId, flags,
- SafeActivityOptions.fromBundle(bOptions), false /* fromRecents */);
+ SafeActivityOptions.fromBundle(bOptions));
}
}
void moveTaskToFrontLocked(@Nullable IApplicationThread appThread,
- @Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options,
- boolean fromRecents) {
+ @Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
assertPackageMatchesCallingUid(callingPackage);
@@ -2527,7 +2526,7 @@
// We are reshowing a task, use a starting window to hide the initial draw delay
// so the transition can start earlier.
topActivity.showStartingWindow(null /* prev */, false /* newTask */,
- true /* taskSwitch */, fromRecents);
+ true /* taskSwitch */);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -3213,7 +3212,7 @@
if (TextUtils.equals(pae.intent.getAction(),
android.service.voice.VoiceInteractionService.SERVICE_INTERFACE)) {
// Start voice interaction through VoiceInteractionManagerService.
- mAssistUtils.showSessionForActiveService(sendBundle, SHOW_SOURCE_APPLICATION,
+ mAssistUtils.showSessionForActiveService(pae.extras, SHOW_SOURCE_APPLICATION,
null, null);
} else {
pae.intent.replaceExtras(pae.extras);
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index 0519b80..529c4f6 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -85,4 +85,25 @@
}
void dumpDebug(ProtoOutputStream proto);
+
+ /**
+ * Gets called when the animation is about to finish and gives the client the opportunity to
+ * defer finishing the animation, i.e. it keeps the leash around until the client calls
+ * endDeferFinishCallback.
+ * <p>
+ * This has the same effect as
+ * {@link com.android.server.wm.SurfaceAnimator.Animatable#shouldDeferAnimationFinish(Runnable)}
+ * . The later will be evaluated first and has precedence over this method if it returns true,
+ * which means that if the {@link com.android.server.wm.SurfaceAnimator.Animatable} requests to
+ * defer its finish, this method won't be called so this adapter will never have access to the
+ * finish callback. On the other hand, if the
+ * {@link com.android.server.wm.SurfaceAnimator.Animatable}, doesn't request to defer, this
+ * {@link AnimationAdapter} is responsible for ending the animation.
+ *
+ * @param endDeferFinishCallback The callback to call when defer finishing should be ended.
+ * @return Whether the client would like to defer the animation finish.
+ */
+ default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e31eaf7..3acb127 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -235,7 +235,6 @@
implements WindowManagerPolicy.DisplayContentInfo {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
- private static final int NO_ROTATION = -1;
/** The default scaling mode that scales content automatically. */
static final int FORCE_SCALING_MODE_AUTO = 0;
@@ -499,6 +498,8 @@
*/
ActivityRecord mFixedRotationLaunchingApp;
+ FixedRotationAnimationController mFixedRotationAnimationController;
+
final FixedRotationTransitionListener mFixedRotationTransitionListener =
new FixedRotationTransitionListener();
@@ -1392,36 +1393,40 @@
final WindowContainer orientationSource = getLastOrientationSource();
final ActivityRecord r =
orientationSource != null ? orientationSource.asActivityRecord() : null;
- if (r != null && r.getTask() != null
- && orientation != r.getTask().mLastReportedRequestedOrientation) {
+ if (r != null) {
final Task task = r.getTask();
- task.mLastReportedRequestedOrientation = orientation;
- mAtmService.getTaskChangeNotificationController()
- .notifyTaskRequestedOrientationChanged(task.mTaskId, orientation);
- }
- // Currently there is no use case from non-activity.
- if (r != null && handleTopActivityLaunchingInDifferentOrientation(r)) {
- // Display orientation should be deferred until the top fixed rotation is finished.
- return false;
+ if (task != null && orientation != task.mLastReportedRequestedOrientation) {
+ task.mLastReportedRequestedOrientation = orientation;
+ mAtmService.getTaskChangeNotificationController()
+ .notifyTaskRequestedOrientationChanged(task.mTaskId, orientation);
+ }
+ // Currently there is no use case from non-activity.
+ if (handleTopActivityLaunchingInDifferentOrientation(r, true /* checkOpening */)) {
+ // Display orientation should be deferred until the top fixed rotation is finished.
+ return false;
+ }
}
return mDisplayRotation.updateOrientation(orientation, forceUpdate);
}
- /** @return a valid rotation if the activity can use different orientation than the display. */
+ /**
+ * Returns a valid rotation if the activity can use different orientation than the display.
+ * Otherwise {@link #ROTATION_UNDEFINED}.
+ */
@Surface.Rotation
- private int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) {
+ int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) {
if (!mWmService.mIsFixedRotationTransformEnabled) {
- return NO_ROTATION;
+ return ROTATION_UNDEFINED;
}
if (r.inMultiWindowMode()
|| r.getRequestedConfigurationOrientation() == getConfiguration().orientation) {
- return NO_ROTATION;
+ return ROTATION_UNDEFINED;
}
final int currentRotation = getRotation();
final int rotation = mDisplayRotation.rotationForOrientation(r.getRequestedOrientation(),
currentRotation);
if (rotation == currentRotation) {
- return NO_ROTATION;
+ return ROTATION_UNDEFINED;
}
return rotation;
}
@@ -1431,9 +1436,13 @@
* is launching until the launch animation is done to avoid showing the previous activity
* inadvertently in a wrong orientation.
*
+ * @param r The launching activity which may change display orientation.
+ * @param checkOpening Whether to check if the activity is animating by transition. Set to
+ * {@code true} if the caller is not sure whether the activity is launching.
* @return {@code true} if the fixed rotation is started.
*/
- private boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r) {
+ boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r,
+ boolean checkOpening) {
if (!mWmService.mIsFixedRotationTransformEnabled) {
return false;
}
@@ -1444,19 +1453,18 @@
// It has been set and not yet finished.
return true;
}
- if (!mAppTransition.isTransitionSet()) {
- // Apply normal rotation animation in case of the activity set different requested
- // orientation without activity switch.
- return false;
- }
- if (!mOpeningApps.contains(r)
- // Without screen rotation, the rotation behavior of non-top visible activities is
- // undefined. So the fixed rotated activity needs to cover the screen.
- && r.findMainWindow() != mDisplayPolicy.getTopFullscreenOpaqueWindow()) {
+ if (checkOpening) {
+ if (!mAppTransition.isTransitionSet() && !mOpeningApps.contains(r)) {
+ // Apply normal rotation animation in case of the activity set different requested
+ // orientation without activity switch.
+ return false;
+ }
+ } else if (r != topRunningActivity()) {
+ // If the transition has not started yet, the activity must be the top.
return false;
}
final int rotation = rotationForActivityInDifferentOrientation(r);
- if (rotation == NO_ROTATION) {
+ if (rotation == ROTATION_UNDEFINED) {
return false;
}
if (!r.getParent().matchParentBounds()) {
@@ -1509,6 +1517,10 @@
sendNewConfiguration();
return;
}
+ if (mDisplayRotation.isWaitingForRemoteRotation()) {
+ // There is pending rotation change to apply.
+ return;
+ }
// The orientation of display is not changed.
clearFixedRotationLaunchingApp();
}
@@ -1528,6 +1540,11 @@
}
private void startFixedRotationTransform(WindowToken token, int rotation) {
+ if (mFixedRotationAnimationController == null) {
+ mFixedRotationAnimationController = new FixedRotationAnimationController(
+ this);
+ }
+ mFixedRotationAnimationController.hide(rotation);
mTmpConfiguration.unset();
final DisplayInfo info = computeScreenConfiguration(mTmpConfiguration, rotation);
final WmDisplayCutout cutout = calculateDisplayCutoutForRotation(rotation);
@@ -1544,11 +1561,18 @@
*/
void rotateInDifferentOrientationIfNeeded(ActivityRecord activityRecord) {
int rotation = rotationForActivityInDifferentOrientation(activityRecord);
- if (rotation != NO_ROTATION) {
+ if (rotation != ROTATION_UNDEFINED) {
startFixedRotationTransform(activityRecord, rotation);
}
}
+ void finishFixedRotationAnimation() {
+ if (mFixedRotationAnimationController != null
+ && mFixedRotationAnimationController.show()) {
+ mFixedRotationAnimationController = null;
+ }
+ }
+
/**
* Update rotation of the display.
*
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index eb5cff6..2e18fbf 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -874,10 +874,6 @@
}
break;
- case TYPE_SCREENSHOT:
- attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- break;
-
case TYPE_TOAST:
// While apps should use the dedicated toast APIs to add such windows
// it possible legacy apps to add the window directly. Therefore, we
@@ -2036,9 +2032,13 @@
final Rect dfu = displayFrames.mUnrestricted;
Insets insets = Insets.of(0, 0, 0, 0);
for (int i = types.size() - 1; i >= 0; i--) {
- insets = Insets.max(insets, mDisplayContent.getInsetsPolicy()
- .getInsetsForDispatch(win).getSource(types.valueAt(i))
- .calculateInsets(dfu, attrs.isFitInsetsIgnoringVisibility()));
+ final InsetsSource source = mDisplayContent.getInsetsPolicy()
+ .getInsetsForDispatch(win).peekSource(types.valueAt(i));
+ if (source == null) {
+ continue;
+ }
+ insets = Insets.max(insets, source.calculateInsets(
+ dfu, attrs.isFitInsetsIgnoringVisibility()));
}
final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index ebfe70c..c3f9061 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -560,6 +560,7 @@
}, true /* traverseTopToBottom */);
mSeamlessRotationCount = 0;
mRotatingSeamlessly = false;
+ mDisplayContent.finishFixedRotationAnimation();
}
private void prepareSeamlessRotation() {
@@ -646,6 +647,7 @@
"Performing post-rotate rotation after seamless rotation");
// Finish seamless rotation.
mRotatingSeamlessly = false;
+ mDisplayContent.finishFixedRotationAnimation();
updateRotationAndSendNewConfigIfChanged();
}
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 9bbd4cd..5a24847 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -135,6 +135,7 @@
final int mOwnerPid;
final WindowManagerService mWmService;
InputChannel mInputChannel;
+ final int mWindowType;
/**
* @param clientToken client token used to clean up the map if the embedding process dies
@@ -146,7 +147,7 @@
* @param ownerPid calling pid used for anr blaming
*/
EmbeddedWindow(WindowManagerService service, IWindow clientToken,
- WindowState hostWindowState, int ownerUid, int ownerPid) {
+ WindowState hostWindowState, int ownerUid, int ownerPid, int windowType) {
mWmService = service;
mClient = clientToken;
mHostWindowState = hostWindowState;
@@ -154,6 +155,7 @@
: null;
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
+ mWindowType = windowType;
}
String getName() {
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index c4e03f5..c92de2b 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -21,7 +21,6 @@
import static com.android.server.wm.ActivityStack.TAG_VISIBILITY;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
-import android.annotation.Nullable;
import android.util.Slog;
import com.android.internal.util.function.pooled.PooledConsumer;
@@ -43,16 +42,6 @@
mContiner = container;
}
- /**
- * Update all attributes except {@link mContiner} to use in subsequent calculations.
- *
- * @param starting The activity that is being started
- * @param configChanges Parts of the configuration that changed for this activity for evaluating
- * if the screen should be frozen.
- * @param preserveWindows Flag indicating whether windows should be preserved when updating.
- * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
- * be sent to the clients.
- */
void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
boolean notifyClients) {
mStarting = starting;
@@ -71,17 +60,8 @@
* Ensure visibility with an option to also update the configuration of visible activities.
* @see ActivityStack#ensureActivitiesVisible(ActivityRecord, int, boolean)
* @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
- * @param starting The top most activity in the task.
- * The activity is either starting or resuming.
- * Caller should ensure starting activity is visible.
- *
- * @param configChanges Parts of the configuration that changed for this activity for evaluating
- * if the screen should be frozen.
- * @param preserveWindows Flag indicating whether windows should be preserved when updating.
- * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
- * be sent to the clients.
*/
- void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows,
+ void process(ActivityRecord starting, int configChanges, boolean preserveWindows,
boolean notifyClients) {
reset(starting, configChanges, preserveWindows, notifyClients);
diff --git a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
new file mode 100644
index 0000000..7aca637
--- /dev/null
+++ b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
@@ -0,0 +1,197 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.AnimationSpecProto.WINDOW;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
+import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
+
+import android.content.res.Configuration;
+import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Transformation;
+
+import com.android.internal.R;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Controller to fade out and in system ui when applying a fixed rotation transform to a window
+ * token.
+ *
+ * The system bars will be fade out when the fixed rotation transform starts and will be fade in
+ * once all surfaces have been rotated.
+ */
+public class FixedRotationAnimationController {
+
+ private final WindowManagerService mWmService;
+ private boolean mShowRequested = true;
+ private int mTargetRotation = Configuration.ORIENTATION_UNDEFINED;
+ private final ArrayList<WindowState> mAnimatedWindowStates = new ArrayList<>(2);
+ private final Runnable[] mDeferredFinishCallbacks;
+
+ public FixedRotationAnimationController(DisplayContent displayContent) {
+ mWmService = displayContent.mWmService;
+ addAnimatedWindow(displayContent.getDisplayPolicy().getStatusBar());
+ addAnimatedWindow(displayContent.getDisplayPolicy().getNavigationBar());
+ mDeferredFinishCallbacks = new Runnable[mAnimatedWindowStates.size()];
+ }
+
+ private void addAnimatedWindow(WindowState windowState) {
+ if (windowState != null) {
+ mAnimatedWindowStates.add(windowState);
+ }
+ }
+
+ /**
+ * Show the previously hidden {@link WindowToken} if their surfaces have already been rotated.
+ *
+ * @return True if the show animation has been started, in which case the caller no longer needs
+ * this {@link FixedRotationAnimationController}.
+ */
+ boolean show() {
+ if (!mShowRequested && readyToShow()) {
+ mShowRequested = true;
+ for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
+ WindowState windowState = mAnimatedWindowStates.get(i);
+ fadeWindowToken(true, windowState.getParent(), i);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void hide(int targetRotation) {
+ mTargetRotation = targetRotation;
+ if (mShowRequested) {
+ mShowRequested = false;
+ for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
+ WindowState windowState = mAnimatedWindowStates.get(i);
+ fadeWindowToken(false /* show */, windowState.getParent(), i);
+ }
+ }
+ }
+
+ void cancel() {
+ for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
+ WindowState windowState = mAnimatedWindowStates.get(i);
+ mShowRequested = true;
+ fadeWindowToken(true /* show */, windowState.getParent(), i);
+ }
+ }
+
+ private void fadeWindowToken(boolean show, WindowContainer<WindowToken> windowToken,
+ int index) {
+ Animation animation = AnimationUtils.loadAnimation(mWmService.mContext,
+ show ? R.anim.fade_in : R.anim.fade_out);
+ LocalAnimationAdapter.AnimationSpec windowAnimationSpec = createAnimationSpec(animation);
+
+ FixedRotationAnimationAdapter animationAdapter = new FixedRotationAnimationAdapter(
+ windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, index);
+
+ // We deferred the end of the animation when hiding the token, so we need to end it now that
+ // it's shown again.
+ SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
+ if (mDeferredFinishCallbacks[index] != null) {
+ mDeferredFinishCallbacks[index].run();
+ mDeferredFinishCallbacks[index] = null;
+ }
+ } : null;
+ windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter,
+ mShowRequested, ANIMATION_TYPE_FIXED_TRANSFORM, finishedCallback);
+ }
+
+ /**
+ * Check if all the mAnimatedWindowState's surfaces have been rotated to the
+ * mTargetRotation.
+ */
+ private boolean readyToShow() {
+ for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
+ WindowState windowState = mAnimatedWindowStates.get(i);
+ if (windowState.getConfiguration().windowConfiguration.getRotation()
+ != mTargetRotation || windowState.mPendingSeamlessRotate != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ private LocalAnimationAdapter.AnimationSpec createAnimationSpec(Animation animation) {
+ return new LocalAnimationAdapter.AnimationSpec() {
+
+ Transformation mTransformation = new Transformation();
+
+ @Override
+ public boolean getShowWallpaper() {
+ return true;
+ }
+
+ @Override
+ public long getDuration() {
+ return animation.getDuration();
+ }
+
+ @Override
+ public void apply(SurfaceControl.Transaction t, SurfaceControl leash,
+ long currentPlayTime) {
+ mTransformation.clear();
+ animation.getTransformation(currentPlayTime, mTransformation);
+ t.setAlpha(leash, mTransformation.getAlpha());
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println(animation);
+ }
+
+ @Override
+ public void dumpDebugInner(ProtoOutputStream proto) {
+ final long token = proto.start(WINDOW);
+ proto.write(ANIMATION, animation.toString());
+ proto.end(token);
+ }
+ };
+ }
+
+ private class FixedRotationAnimationAdapter extends LocalAnimationAdapter {
+ private final boolean mShow;
+ private final int mIndex;
+
+ FixedRotationAnimationAdapter(AnimationSpec windowAnimationSpec,
+ SurfaceAnimationRunner surfaceAnimationRunner, boolean show, int index) {
+ super(windowAnimationSpec, surfaceAnimationRunner);
+ mShow = show;
+ mIndex = index;
+ }
+
+ @Override
+ public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+ // We defer the end of the hide animation to ensure the tokens stay hidden until
+ // we show them again.
+ if (!mShow) {
+ mDeferredFinishCallbacks[mIndex] = endDeferFinishCallback;
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index c93b735..a2979e6 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -327,7 +327,7 @@
documentData = isDocument ? intent.getData() : null;
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + target + " in " + parent);
- parent.forAllTasks(this);
+ parent.forAllLeafTasks(this);
}
void clear() {
@@ -2188,6 +2188,10 @@
// display area, so reparent.
stack.reparent(taskDisplayArea, true /* onTop */);
}
+ // Defer the windowing mode change until after the transition to prevent the activity
+ // from doing work and changing the activity visuals while animating
+ // TODO(task-org): Figure-out more structured way to do this long term.
+ r.setWindowingMode(stack.getWindowingMode());
stack.setWindowingMode(WINDOWING_MODE_PINNED);
// Reset the state that indicates it can enter PiP while pausing after we've moved it
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index bf20cb9..e225809 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -91,6 +91,7 @@
private float mLastReportedAnimatorScale;
private String mPackageName;
private String mRelayoutTag;
+ private final InsetsSourceControl[] mDummyControls = new InsetsSourceControl[0];
public Session(WindowManagerService service, IWindowSessionCallback callback) {
mService = service;
@@ -184,7 +185,7 @@
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
new Rect() /* outFrame */, outContentInsets, outStableInsets,
new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
- outInsetsState, null, UserHandle.getUserId(mUid));
+ outInsetsState, mDummyControls, UserHandle.getUserId(mUid));
}
@Override
@@ -661,17 +662,23 @@
@Override
public void grantInputChannel(int displayId, SurfaceControl surface,
- IWindow window, IBinder hostInputToken, int flags, InputChannel outInputChannel) {
+ IWindow window, IBinder hostInputToken, int flags, int type,
+ InputChannel outInputChannel) {
if (hostInputToken == null && !mCanAddInternalSystemWindow) {
// Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
// embedded windows without providing a host window input token
throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
}
+ if (!mCanAddInternalSystemWindow && type != 0) {
+ Slog.w(TAG_WM, "Requires INTERNAL_SYSTEM_WINDOW permission if assign type to"
+ + " input");
+ }
+
final long identity = Binder.clearCallingIdentity();
try {
mService.grantInputChannel(mUid, mPid, displayId, surface, window, hostInputToken,
- flags, outInputChannel);
+ flags, mCanAddInternalSystemWindow ? type : 0, outInputChannel);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 18e32c0..0143eb1 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -109,7 +109,10 @@
animationFinishCallback.onAnimationFinished(type, anim);
}
};
- if (!mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)) {
+ // If both the Animatable and AnimationAdapter requests to be deferred, only the
+ // first one will be called.
+ if (!(mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)
+ || anim.shouldDeferAnimationFinish(resetAndInvokeFinish))) {
resetAndInvokeFinish.run();
}
}
@@ -486,6 +489,12 @@
static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5;
/**
+ * Animation when a fixed rotation transform is applied to a window token.
+ * @hide
+ */
+ static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6;
+
+ /**
* Bitmask to include all animation types. This is NOT an {@link AnimationType}
* @hide
*/
@@ -502,7 +511,8 @@
ANIMATION_TYPE_DIMMER,
ANIMATION_TYPE_RECENTS,
ANIMATION_TYPE_WINDOW_ANIMATION,
- ANIMATION_TYPE_INSETS_CONTROL
+ ANIMATION_TYPE_INSETS_CONTROL,
+ ANIMATION_TYPE_FIXED_TRANSFORM
})
@Retention(RetentionPolicy.SOURCE)
@interface AnimationType {}
@@ -592,6 +602,12 @@
* Gets called when the animation is about to finish and gives the client the opportunity to
* defer finishing the animation, i.e. it keeps the leash around until the client calls
* {@link #cancelAnimation}.
+ * <p>
+ * {@link AnimationAdapter} has a similar method which is called only if this method returns
+ * false. This mean that if both this {@link Animatable} and the {@link AnimationAdapter}
+ * request to be deferred, this method is the sole responsible to call
+ * endDeferFinishCallback. On the other hand, the animation finish might still be deferred
+ * if this method return false and the one from the {@link AnimationAdapter} returns true.
*
* @param endDeferFinishCallback The callback to call when defer finishing should be ended.
* @return Whether the client would like to defer the animation finish.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b9e6513..cc72dcf 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -942,7 +942,7 @@
return;
}
- affinity = info.taskAffinity;
+ affinity = isLeafTask() ? info.taskAffinity : null;
if (intent == null) {
// If this task already has an intent associated with it, don't set the root
// affinity -- we don't want it changing after initially set, but the initially
@@ -1940,7 +1940,6 @@
setLastNonFullscreenBounds(currentBounds);
}
}
- // TODO: Should also take care of Pip mode changes here.
saveLaunchingStateIfNeeded();
final boolean taskOrgChanged = updateTaskOrganizerState(false /* forceUpdate */);
@@ -3400,6 +3399,24 @@
}
@Override
+ boolean forAllLeafTasks(Function<Task, Boolean> callback) {
+ boolean isLeafTask = true;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final Task child = mChildren.get(i).asTask();
+ if (child != null) {
+ isLeafTask = false;
+ if (child.forAllLeafTasks(callback)) {
+ return true;
+ }
+ }
+ }
+ if (isLeafTask) {
+ return callback.apply(this);
+ }
+ return false;
+ }
+
+ @Override
Task getTask(Predicate<Task> callback, boolean traverseTopToBottom) {
final Task t = super.getTask(callback, traverseTopToBottom);
if (t != null) return t;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 0f5cafe..1a2672b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -297,6 +297,13 @@
Slog.w(TAG_WM, "Failed to take screenshot. No main window for " + task);
return false;
}
+ if (activity.hasFixedRotationTransform()) {
+ if (DEBUG_SCREENSHOT) {
+ Slog.i(TAG_WM, "Skip taking screenshot. App has fixed rotation " + activity);
+ }
+ // The activity is in a temporal state that it has different rotation than the task.
+ return false;
+ }
builder.setIsRealSnapshot(true);
builder.setId(System.currentTimeMillis());
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 24cd7d1..3925570 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -195,6 +195,16 @@
+ activity);
return null;
}
+ if (topFullscreenActivity.getWindowConfiguration().getRotation()
+ != snapshot.getRotation()) {
+ // The snapshot should have been checked by ActivityRecord#isSnapshotCompatible
+ // that the activity will be updated to the same rotation as the snapshot. Since
+ // the transition is not started yet, fixed rotation transform needs to be applied
+ // earlier to make the snapshot show in a rotated container.
+ activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(
+ topFullscreenActivity, false /* checkOpening */);
+ }
+
sysUiVis = topFullscreenOpaqueWindow.getSystemUiVisibility();
WindowManager.LayoutParams attrs = topFullscreenOpaqueWindow.mAttrs;
windowFlags = attrs.flags;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 3f8d7b5..5d7ec12 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1552,6 +1552,15 @@
return false;
}
+ boolean forAllLeafTasks(Function<Task, Boolean> callback) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).forAllLeafTasks(callback)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* For all tasks at or below this container call the callback.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4c1d6f3..5c21b2b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1387,6 +1387,7 @@
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
+ Arrays.fill(outActiveControls, null);
int[] appOp = new int[1];
final boolean isRoundedCornerOverlay = (attrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
@@ -2133,6 +2134,7 @@
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
SurfaceControl outBLASTSurfaceControl) {
+ Arrays.fill(outActiveControls, null);
int result = 0;
boolean configChanged;
final int pid = Binder.getCallingPid();
@@ -2470,23 +2472,20 @@
}
private void getInsetsSourceControls(WindowState win, InsetsSourceControl[] outControls) {
- if (outControls != null) {
- final InsetsSourceControl[] controls =
- win.getDisplayContent().getInsetsStateController().getControlsForDispatch(win);
- Arrays.fill(outControls, null);
- if (controls != null) {
- final int length = Math.min(controls.length, outControls.length);
- for (int i = 0; i < length; i++) {
- // We will leave the critical section before returning the leash to the client,
- // so we need to copy the leash to prevent others release the one that we are
- // about to return.
- // TODO: We will have an extra copy if the client is not local.
- // For now, we rely on GC to release it.
- // Maybe we can modify InsetsSourceControl.writeToParcel so it can release
- // the extra leash as soon as possible.
- outControls[i] = controls[i] != null
- ? new InsetsSourceControl(controls[i]) : null;
- }
+ final InsetsSourceControl[] controls =
+ win.getDisplayContent().getInsetsStateController().getControlsForDispatch(win);
+ if (controls != null) {
+ final int length = Math.min(controls.length, outControls.length);
+ for (int i = 0; i < length; i++) {
+ // We will leave the critical section before returning the leash to the client,
+ // so we need to copy the leash to prevent others release the one that we are
+ // about to return.
+ // TODO: We will have an extra copy if the client is not local.
+ // For now, we rely on GC to release it.
+ // Maybe we can modify InsetsSourceControl.writeToParcel so it can release
+ // the extra leash as soon as possible.
+ outControls[i] = controls[i] != null
+ ? new InsetsSourceControl(controls[i]) : null;
}
}
}
@@ -8027,14 +8026,15 @@
* views.
*/
void grantInputChannel(int callingUid, int callingPid, int displayId, SurfaceControl surface,
- IWindow window, IBinder hostInputToken, int flags, InputChannel outInputChannel) {
+ IWindow window, IBinder hostInputToken, int flags, int type,
+ InputChannel outInputChannel) {
final InputApplicationHandle applicationHandle;
final String name;
final InputChannel clientChannel;
synchronized (mGlobalLock) {
EmbeddedWindowController.EmbeddedWindow win =
new EmbeddedWindowController.EmbeddedWindow(this, window,
- mInputToWindowMap.get(hostInputToken), callingUid, callingPid);
+ mInputToWindowMap.get(hostInputToken), callingUid, callingPid, type);
clientChannel = win.openInputChannel();
mEmbeddedWindowController.add(clientChannel.getToken(), win);
applicationHandle = win.getApplicationHandle();
@@ -8042,7 +8042,7 @@
}
updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface,
- name, applicationHandle, flags, null /* region */);
+ name, applicationHandle, flags, type, null /* region */);
clientChannel.transferTo(outInputChannel);
clientChannel.dispose();
@@ -8050,7 +8050,7 @@
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
int displayId, SurfaceControl surface, String name,
- InputApplicationHandle applicationHandle, int flags, Region region) {
+ InputApplicationHandle applicationHandle, int flags, int type, Region region) {
InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
h.token = channelToken;
h.name = name;
@@ -8058,7 +8058,7 @@
final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE
| LayoutParams.FLAG_SLIPPERY);
h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags;
- h.layoutParamsType = 0;
+ h.layoutParamsType = type;
h.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
h.canReceiveKeys = false;
h.hasFocus = false;
@@ -8082,6 +8082,7 @@
t.setInputWindowInfo(surface, h);
t.apply();
t.close();
+ surface.release();
}
/**
@@ -8105,7 +8106,7 @@
}
updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
- applicationHandle, flags, region);
+ applicationHandle, flags, win.mWindowType, region);
}
/** Return whether layer tracing is enabled */
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index c4cb4b5..707a789 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -138,6 +138,13 @@
Slog.e(TAG, "Attempt to operate on detached container: " + wc);
continue;
}
+ // Make sure we add to the syncSet before performing
+ // operations so we don't end up splitting effects between the WM
+ // pending transaction and the BLASTSync transaction.
+ if (syncId >= 0) {
+ mBLASTSyncEngine.addToSyncSet(syncId, wc);
+ }
+
int containerEffect = applyWindowContainerChange(wc, entry.getValue());
effects |= containerEffect;
@@ -146,9 +153,6 @@
&& (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
haveConfigChanges.add(wc);
}
- if (syncId >= 0) {
- mBLASTSyncEngine.addToSyncSet(syncId, wc);
- }
}
// Hierarchy changes
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e925ce5..a948ece 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2191,9 +2191,9 @@
if (wasVisible) {
final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
-
+ final int flags = startingWindow ? 0 /* self */ : PARENTS;
// Try starting an animation.
- if (mWinAnimator.applyAnimationLocked(transit, false)) {
+ if (mWinAnimator.applyAnimationLocked(transit, false, flags)) {
mAnimatingExit = true;
// mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
@@ -2205,7 +2205,9 @@
mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
}
}
- final boolean isAnimating = isAnimating(TRANSITION | PARENTS)
+ final boolean isAnimating = startingWindow
+ ? isAnimating(0)
+ : isAnimating(TRANSITION | PARENTS)
&& (mActivityRecord == null || !mActivityRecord.isWaitingForTransitionStart());
final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
&& mActivityRecord.isLastWindow(this);
@@ -2227,6 +2229,9 @@
}
}
+ if (startingWindow && mActivityRecord != null) {
+ mActivityRecord.startingDisplayed = false;
+ }
removeImmediately();
// Removing a visible window will effect the computed orientation
// So just update orientation if needed.
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c570cf1..e70f3e4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1400,9 +1400,25 @@
* the switch statement below.
* @param isEntrance The animation type the last time this was called. Used to keep from
* loading the same animation twice.
- * @return true if an animation has been loaded.
+ * @return {@code true} if an animation has been loaded, includes the parents.
+ *
*/
boolean applyAnimationLocked(int transit, boolean isEntrance) {
+ return applyAnimationLocked(transit, isEntrance, PARENTS);
+ }
+
+ /**
+ * Choose the correct animation and set it to the passed WindowState.
+ * @param transit If AppTransition.TRANSIT_PREVIEW_DONE and the app window has been drawn
+ * then the animation will be app_starting_exit. Any other value loads the animation from
+ * the switch statement below.
+ * @param isEntrance The animation type the last time this was called. Used to keep from
+ * loading the same animation twice.
+ * @param flags The combination of bitmask flags to specify targets and condition for
+ * checking animating status. See {@link WindowContainer.AnimationFlags}.
+ * @return {@code true} if an animation has been loaded.
+ */
+ boolean applyAnimationLocked(int transit, boolean isEntrance, int flags) {
if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) {
// If we are trying to apply an animation, but already running
// an animation of the same type, then just leave that one alone.
@@ -1472,7 +1488,7 @@
mWin.getDisplayContent().adjustForImeIfNeeded();
}
- return mWin.isAnimating(PARENTS);
+ return mWin.isAnimating(flags);
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 768f89e..8739bad4 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -642,6 +642,9 @@
final int originalRotation = getWindowConfiguration().getRotation();
onConfigurationChanged(parent.getConfiguration());
onCancelFixedRotationTransform(originalRotation);
+ if (mDisplayContent.mFixedRotationAnimationController != null) {
+ mDisplayContent.mFixedRotationAnimationController.cancel();
+ }
}
/**
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 6cf8133..e904645 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "PackageManagerShellCommandDataLoader-jni"
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/no_destructor.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <core_jni_helpers.h>
@@ -65,6 +66,7 @@
static constexpr MagicType INCR = 0x52434e49; // BE INCR
static constexpr auto PollTimeoutMs = 5000;
+static constexpr auto TraceTagCheckInterval = 1s;
struct JniIds {
jclass packageManagerShellCommandDataLoader;
@@ -337,9 +339,47 @@
return env;
}
-class PackageManagerShellCommandDataLoaderDataLoader : public android::dataloader::DataLoader {
+class PMSCDataLoader;
+
+struct OnTraceChanged {
+ OnTraceChanged();
+ ~OnTraceChanged() {
+ mRunning = false;
+ mChecker.join();
+ }
+
+ void registerCallback(PMSCDataLoader* callback) {
+ std::unique_lock lock(mMutex);
+ mCallbacks.insert(callback);
+ }
+
+ void unregisterCallback(PMSCDataLoader* callback) {
+ std::unique_lock lock(mMutex);
+ mCallbacks.erase(callback);
+ }
+
+private:
+ std::mutex mMutex;
+ std::unordered_set<PMSCDataLoader*> mCallbacks;
+ std::atomic<bool> mRunning{true};
+ std::thread mChecker;
+};
+
+static OnTraceChanged& onTraceChanged() {
+ static android::base::NoDestructor<OnTraceChanged> instance;
+ return *instance;
+}
+
+class PMSCDataLoader : public android::dataloader::DataLoader {
public:
- PackageManagerShellCommandDataLoaderDataLoader(JavaVM* jvm) : mJvm(jvm) { CHECK(mJvm); }
+ PMSCDataLoader(JavaVM* jvm) : mJvm(jvm) { CHECK(mJvm); }
+ ~PMSCDataLoader() { onTraceChanged().unregisterCallback(this); }
+
+ void updateReadLogsState(const bool enabled) {
+ if (enabled != mReadLogsEnabled.exchange(enabled)) {
+ mIfs->setParams({.readLogsEnabled = enabled});
+ }
+ }
private:
// Lifecycle.
@@ -353,7 +393,8 @@
mArgs = params.arguments();
mIfs = ifs;
mStatusListener = statusListener;
- mIfs->setParams({.readLogsEnabled = true});
+ updateReadLogsState(atrace_is_tag_enabled(ATRACE_TAG));
+ onTraceChanged().registerCallback(this);
return true;
}
bool onStart() final { return true; }
@@ -365,6 +406,7 @@
}
}
void onDestroy() final {
+ onTraceChanged().unregisterCallback(this);
// Make sure the receiver thread stopped.
CHECK(!mReceiverThread.joinable());
}
@@ -757,10 +799,28 @@
android::base::unique_fd mEventFd;
std::thread mReceiverThread;
std::atomic<bool> mStopReceiving = false;
+ std::atomic<bool> mReadLogsEnabled = false;
/** Tracks which files have been requested */
std::unordered_set<FileIdx> mRequestedFiles;
};
+OnTraceChanged::OnTraceChanged() {
+ mChecker = std::thread([this]() {
+ bool oldTrace = atrace_is_tag_enabled(ATRACE_TAG);
+ while (mRunning) {
+ bool newTrace = atrace_is_tag_enabled(ATRACE_TAG);
+ if (oldTrace != newTrace) {
+ std::unique_lock lock(mMutex);
+ for (auto&& callback : mCallbacks) {
+ callback->updateReadLogsState(newTrace);
+ }
+ }
+ oldTrace = newTrace;
+ std::this_thread::sleep_for(TraceTagCheckInterval);
+ }
+ });
+}
+
BlockHeader readHeader(std::span<uint8_t>& data) {
BlockHeader header;
if (data.size() < sizeof(header)) {
@@ -794,7 +854,7 @@
[](auto jvm, const auto& params) -> android::dataloader::DataLoaderPtr {
if (params.type() == DATA_LOADER_TYPE_INCREMENTAL) {
// This DataLoader only supports incremental installations.
- return std::make_unique<PackageManagerShellCommandDataLoaderDataLoader>(jvm);
+ return std::make_unique<PMSCDataLoader>(jvm);
}
return {};
});
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index a9dc92f..78439db 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -748,7 +748,7 @@
return -EINVAL;
}
- LOG(INFO) << "Removing bind point " << target;
+ LOG(INFO) << "Removing bind point " << target << " for storage " << storage;
// Here we should only look up by the exact target, not by a subdirectory of any existing mount,
// otherwise there's a chance to unmount something completely unrelated
@@ -1807,6 +1807,8 @@
targetStatus = mTargetStatus;
}
+ LOG(DEBUG) << "fsmStep: " << mId << ": " << currentStatus << " -> " << targetStatus;
+
if (currentStatus == targetStatus) {
return true;
}
@@ -1868,8 +1870,8 @@
listener = mListener;
if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE) {
- // For unavailable, reset target status.
- setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE);
+ // For unavailable, unbind from DataLoader to ensure proper re-commit.
+ setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
}
}
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 325218d..2e4625c 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -677,10 +677,10 @@
mDataLoaderManager->bindToDataLoaderSuccess();
mDataLoaderManager->getDataLoaderSuccess();
EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2);
- EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
EXPECT_CALL(*mDataLoader, start(_)).Times(0);
- EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(2);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(1);
EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(1);
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index f8d197a..d78dad5 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -46,6 +46,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.os.Process;
import android.os.UserHandle;
@@ -106,6 +107,7 @@
new CrossProfileAppsServiceImpl(mContext, mInjector);
private final Map<UserHandle, Set<Intent>> mSentUserBroadcasts = new HashMap<>();
private final Map<Integer, List<ApplicationInfo>> installedApplications = new HashMap<>();
+ private final Set<Integer> mKilledUids = new HashSet<>();
@Mock private PackageManagerInternal mPackageManagerInternal;
@Mock private IPackageManager mIPackageManager;
@@ -389,6 +391,33 @@
}
@Test
+ public void setInteractAcrossProfilesAppOp_toAllowed_doesNotKillApp() {
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
+ assertThat(mKilledUids).isEmpty();
+ }
+
+ @Test
+ public void setInteractAcrossProfilesAppOp_toDisallowed_killsAppsInBothProfiles() {
+ shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo());
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
+
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ CROSS_PROFILE_APP_PACKAGE_NAME, MODE_DEFAULT);
+
+ assertThat(mKilledUids).contains(WORK_PROFILE_UID);
+ assertThat(mKilledUids).contains(PERSONAL_PROFILE_UID);
+ }
+
+ private PermissionInfo createCrossProfilesPermissionInfo() {
+ PermissionInfo permissionInfo = new PermissionInfo();
+ permissionInfo.name = Manifest.permission.INTERACT_ACROSS_PROFILES;
+ permissionInfo.protectionLevel = PermissionInfo.PROTECTION_FLAG_APPOP;
+ return permissionInfo;
+ }
+
+ @Test
public void canConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsFalse() {
mockUninstallCrossProfileAppFromWorkProfile();
assertThat(mCrossProfileAppsServiceImpl
@@ -678,5 +707,10 @@
// ShadowActivityThread with Robolectric. This method is currently not supported there.
return mContext.checkPermission(permission, Process.myPid(), uid);
}
+
+ @Override
+ public void killUid(String packageName, int uid) {
+ mKilledUids.add(uid);
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index e79b5af..a2393a8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -32,8 +32,10 @@
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.PermissionManager;
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
@@ -692,5 +694,17 @@
String permission, int uid, int owningUid, boolean exported) {
return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported);
}
+
+ @Override
+ public void killUid(String packageName, int uid) {
+ try {
+ ActivityManager.getService().killApplication(
+ packageName,
+ UserHandle.getAppId(uid),
+ UserHandle.getUserId(uid),
+ PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED);
+ } catch (RemoteException ignored) {
+ }
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index d136614..6c1c019 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -22,10 +22,14 @@
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.UserManager;
+import android.support.test.uiautomator.UiDevice;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
import android.util.AtomicFile;
+import androidx.test.InstrumentationRegistry;
+
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
@@ -74,6 +78,14 @@
assertEquals(accountName, um.getUserAccount(tempUserId));
}
+ public void testUserSystemPackageWhitelist() throws Exception {
+ String cmd = "cmd user report-system-user-package-whitelist-problems --critical-only";
+ final String result = runShellCommand(cmd);
+ if (!TextUtils.isEmpty(result)) {
+ fail("Command '" + cmd + " reported errors:\n" + result);
+ }
+ }
+
private Bundle createBundle() {
Bundle result = new Bundle();
// Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[]
@@ -118,4 +130,8 @@
assertEquals(1, childBundle.getInt("bundle_int"));
}
+ private static String runShellCommand(String cmd) throws Exception {
+ return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ .executeShellCommand(cmd);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 48e22f6..e410220 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -63,6 +63,7 @@
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -107,6 +108,10 @@
private static final int UID_1 = 10000;
private static final String PACKAGE_EXEMPTED_1 = "com.android.exempted";
private static final int UID_EXEMPTED_1 = 10001;
+ private static final String PACKAGE_SYSTEM_HEADFULL = "com.example.system.headfull";
+ private static final int UID_SYSTEM_HEADFULL = 10002;
+ private static final String PACKAGE_SYSTEM_HEADLESS = "com.example.system.headless";
+ private static final int UID_SYSTEM_HEADLESS = 10003;
private static final int USER_ID = 0;
private static final int USER_ID2 = 10;
private static final UserHandle USER_HANDLE_USER2 = new UserHandle(USER_ID2);
@@ -305,18 +310,33 @@
pie.packageName = PACKAGE_EXEMPTED_1;
packages.add(pie);
+ PackageInfo pis = new PackageInfo();
+ pis.activities = new ActivityInfo[]{mock(ActivityInfo.class)};
+ pis.applicationInfo = new ApplicationInfo();
+ pis.applicationInfo.uid = UID_SYSTEM_HEADFULL;
+ pis.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+ pis.packageName = PACKAGE_SYSTEM_HEADFULL;
+ packages.add(pis);
+
+ PackageInfo pish = new PackageInfo();
+ pish.applicationInfo = new ApplicationInfo();
+ pish.applicationInfo.uid = UID_SYSTEM_HEADLESS;
+ pish.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+ pish.packageName = PACKAGE_SYSTEM_HEADLESS;
+ packages.add(pish);
+
doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
try {
- doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt());
- doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt());
- doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
- anyInt());
- doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
- anyInt(), anyInt());
- doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName),
- anyInt());
- doReturn(pie.applicationInfo).when(mockPm).getApplicationInfo(eq(pie.packageName),
- anyInt());
+ for (int i = 0; i < packages.size(); ++i) {
+ PackageInfo pkg = packages.get(i);
+
+ doReturn(pkg.applicationInfo.uid).when(mockPm)
+ .getPackageUidAsUser(eq(pkg.packageName), anyInt());
+ doReturn(pkg.applicationInfo.uid).when(mockPm)
+ .getPackageUidAsUser(eq(pkg.packageName), anyInt(), anyInt());
+ doReturn(pkg.applicationInfo).when(mockPm)
+ .getApplicationInfo(eq(pkg.packageName), anyInt());
+ }
} catch (PackageManager.NameNotFoundException nnfe) {}
}
@@ -514,7 +534,11 @@
}
private void assertBucket(int bucket) {
- assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1));
+ assertBucket(bucket, PACKAGE_1);
+ }
+
+ private void assertBucket(int bucket, String pkg) {
+ assertEquals(bucket, getStandbyBucket(mController, pkg));
}
private void assertNotBucket(int bucket) {
@@ -1462,6 +1486,32 @@
assertBucket(STANDBY_BUCKET_RESTRICTED);
}
+ @Test
+ public void testSystemHeadlessAppElevated() {
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime,
+ PACKAGE_SYSTEM_HEADFULL);
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime,
+ PACKAGE_SYSTEM_HEADLESS);
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+
+
+ mController.setAppStandbyBucket(PACKAGE_SYSTEM_HEADFULL, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_TIMEOUT);
+ assertBucket(STANDBY_BUCKET_RARE, PACKAGE_SYSTEM_HEADFULL);
+
+ // Make sure headless system apps don't get lowered.
+ mController.setAppStandbyBucket(PACKAGE_SYSTEM_HEADLESS, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_TIMEOUT);
+ assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_SYSTEM_HEADLESS);
+
+ // Package 1 doesn't have activities and is headless, but is not a system app, so it can
+ // be lowered.
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_TIMEOUT);
+ assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
+ }
+
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8cfe96f..4da5adf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -35,6 +35,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
@@ -69,6 +70,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
+import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityConfigurationChangeItem;
@@ -82,14 +84,18 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
import android.util.MutableBoolean;
import android.view.DisplayInfo;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner.Stub;
+import android.view.IWindowSession;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import androidx.test.filters.MediumTest;
@@ -488,6 +494,16 @@
}
@Test
+ public void testShouldMakeActive_nonTopVisible() {
+ ActivityRecord finishingActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ finishingActivity.finishing = true;
+ ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
+
+ assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */));
+ }
+
+ @Test
public void testShouldResume_stackVisibility() {
mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
spyOn(mStack);
@@ -1394,6 +1410,10 @@
assertTrue(displayRotation.isRotatingSeamlessly());
+ // The launching rotated app should not be cleared when waiting for remote rotation.
+ display.continueUpdateOrientationForDiffOrienLaunchingApp();
+ assertNotNull(display.mFixedRotationLaunchingApp);
+
// Simulate the rotation has been updated to previous one, e.g. sensor updates before the
// remote rotation is completed.
doReturn(originalRotation).when(displayRotation).rotationForOrientation(
@@ -1426,6 +1446,73 @@
}
@Test
+ public void testIsSnapshotCompatible() {
+ mService.mWindowManager.mIsFixedRotationTransformEnabled = true;
+ final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
+ .setRotation(mActivity.getWindowConfiguration().getRotation())
+ .build();
+
+ assertTrue(mActivity.isSnapshotCompatible(snapshot));
+
+ setRotatedScreenOrientationSilently(mActivity);
+
+ assertFalse(mActivity.isSnapshotCompatible(snapshot));
+ }
+
+ @Test
+ public void testFixedRotationSnapshotStartingWindow() {
+ mService.mWindowManager.mIsFixedRotationTransformEnabled = true;
+ // TaskSnapshotSurface requires a fullscreen opaque window.
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
+ params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT;
+ final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
+ mService.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity);
+ mActivity.addWindow(w);
+
+ // Assume the activity is launching in different rotation, and there was an available
+ // snapshot accepted by {@link Activity#isSnapshotCompatible}.
+ final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
+ .setRotation((mActivity.getWindowConfiguration().getRotation() + 1) % 4)
+ .build();
+ setRotatedScreenOrientationSilently(mActivity);
+
+ final IWindowSession session = WindowManagerGlobal.getWindowSession();
+ spyOn(session);
+ try {
+ // Return error to skip unnecessary operation.
+ doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay(
+ any() /* window */, anyInt() /* seq */, any() /* attrs */,
+ anyInt() /* viewVisibility */, anyInt() /* displayId */, any() /* outFrame */,
+ any() /* outContentInsets */, any() /* outStableInsets */,
+ any() /* outDisplayCutout */, any() /* outInputChannel */,
+ any() /* outInsetsState */, any() /* outActiveControls */);
+ TaskSnapshotSurface.create(mService.mWindowManager, mActivity, snapshot);
+ } catch (RemoteException ignored) {
+ } finally {
+ reset(session);
+ }
+
+ // Because the rotation of snapshot and the corresponding top activity are different, fixed
+ // rotation should be applied when creating snapshot surface if the display rotation may be
+ // changed according to the activity orientation.
+ assertTrue(mActivity.hasFixedRotationTransform());
+ assertEquals(mActivity, mActivity.mDisplayContent.mFixedRotationLaunchingApp);
+ }
+
+ /**
+ * Sets orientation without notifying the parent to simulate that the display has not applied
+ * the requested orientation yet.
+ */
+ private static void setRotatedScreenOrientationSilently(ActivityRecord r) {
+ final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT
+ ? SCREEN_ORIENTATION_LANDSCAPE
+ : SCREEN_ORIENTATION_PORTRAIT;
+ doReturn(false).when(r).onDescendantOrientationChanged(any(), any());
+ r.setOrientation(rotatedOrentation);
+ }
+
+ @Test
public void testActivityOnDifferentDisplayUpdatesProcessOverride() {
final ActivityRecord secondaryDisplayActivity =
createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 02d1f9b..edc9756 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -49,7 +49,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -57,10 +56,10 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -100,6 +99,7 @@
@Presubmit
@RunWith(WindowTestRunner.class)
public class ActivityStarterTests extends ActivityTestsBase {
+ private ActivityStarter mStarter;
private ActivityStartController mController;
private ActivityMetricsLogger mActivityMetricsLogger;
private PackageManagerInternal mMockPackageManager;
@@ -127,6 +127,8 @@
mController = mock(ActivityStartController.class);
mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
clearInvocations(mActivityMetricsLogger);
+ mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
+ mock(ActivityStartInterceptor.class));
}
@Test
@@ -179,7 +181,6 @@
* {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
* the expected. It is important to note that the method also checks side effects of the start,
* such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
- *
* @param preconditions A bitmask representing the preconditions for the launch
* @param launchFlags The launch flags to be provided by the launch {@link Intent}.
* @param expectedResult The expected result from the launch.
@@ -201,7 +202,7 @@
final WindowProcessController wpc =
containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
? null : new WindowProcessController(service, ai, null, 0, -1, null, listener);
- doReturn(wpc).when(service).getProcessController(any());
+ doReturn(wpc).when(service).getProcessController(anyObject());
final Intent intent = new Intent();
intent.setFlags(launchFlags);
@@ -1033,46 +1034,4 @@
verify(recentTasks, times(1)).add(any());
}
-
- @Test
- public void testStartActivityInner_allSplitScreenPrimaryActivitiesVisible() {
- // Given
- final ActivityStarter starter = prepareStarter(0, false);
-
- starter.setReason("testAllSplitScreenPrimaryActivitiesAreResumed");
-
- final ActivityRecord targetRecord = new ActivityBuilder(mService).build();
- targetRecord.setFocusable(false);
- targetRecord.setVisibility(false);
- final ActivityRecord sourceRecord = new ActivityBuilder(mService).build();
-
- final ActivityStack stack = spy(
- mRootWindowContainer.getDefaultTaskDisplayArea()
- .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
- /* onTop */true));
-
- stack.addChild(targetRecord);
-
- doReturn(stack).when(mRootWindowContainer)
- .getLaunchStack(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt());
-
- starter.mStartActivity = new ActivityBuilder(mService).build();
-
- // When
- starter.startActivityInner(
- /* r */targetRecord,
- /* sourceRecord */ sourceRecord,
- /* voiceSession */null,
- /* voiceInteractor */ null,
- /* startFlags */ 0,
- /* doResume */true,
- /* options */null,
- /* inTask */null,
- /* restrictedBgActivity */false);
-
- // Then
- verify(stack).ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- verify(targetRecord).makeVisibleIfNeeded(null, true);
- assertTrue(targetRecord.mVisibleRequested);
- }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 5c21853..8f18f64 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -307,7 +307,7 @@
public void testCreateRemoveStartingWindow() {
mActivity.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
+ false);
waitUntilHandlersIdle();
assertHasStartingWindow(mActivity);
mActivity.removeStartingWindow();
@@ -316,20 +316,16 @@
}
@Test
- @FlakyTest(bugId = 130392471)
public void testAddRemoveRace() {
// There was once a race condition between adding and removing starting windows
+ final ActivityRecord appToken = mAppWindow.mActivityRecord;
for (int i = 0; i < 1000; i++) {
- final ActivityRecord appToken = createIsolatedTestActivityRecord();
-
appToken.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
+ false);
appToken.removeStartingWindow();
waitUntilHandlersIdle();
assertNoStartingWindow(appToken);
-
- appToken.getParent().getParent().removeImmediately();
}
}
@@ -339,11 +335,11 @@
final ActivityRecord activity2 = createIsolatedTestActivityRecord();
activity1.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
+ false);
waitUntilHandlersIdle();
activity2.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
- true, true, false, true, false, false);
+ true, true, false, true, false);
waitUntilHandlersIdle();
assertNoStartingWindow(activity1);
assertHasStartingWindow(activity2);
@@ -359,11 +355,11 @@
activity2.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0,
activity1.appToken.asBinder(), true, true, false,
- true, false, false);
+ true, false);
});
activity1.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
+ false);
waitUntilHandlersIdle();
assertNoStartingWindow(activity1);
assertHasStartingWindow(activity2);
@@ -375,11 +371,11 @@
final ActivityRecord activity2 = createIsolatedTestActivityRecord();
activity1.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
+ false);
waitUntilHandlersIdle();
activity2.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
- true, true, false, true, false, false);
+ true, true, false, true, false);
waitUntilHandlersIdle();
assertNoStartingWindow(activity1);
assertHasStartingWindow(activity2);
@@ -417,7 +413,7 @@
// Add a starting window.
activityTop.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
+ false);
waitUntilHandlersIdle();
// Make the top one invisible, and try transferring the starting window from the top to the
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index ac95a81..7b23bfb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -57,6 +57,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -1059,6 +1060,8 @@
@Test
public void testApplyTopFixedRotationTransform() {
mWm.mIsFixedRotationTransformEnabled = true;
+ mDisplayContent.getDisplayPolicy().addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
+ mDisplayContent.getDisplayPolicy().addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
final Configuration config90 = new Configuration();
mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);
@@ -1079,6 +1082,12 @@
ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
false /* forceUpdate */));
+ assertNotNull(mDisplayContent.mFixedRotationAnimationController);
+ assertTrue(mStatusBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
+ ANIMATION_TYPE_FIXED_TRANSFORM));
+ assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
+ ANIMATION_TYPE_FIXED_TRANSFORM));
+
final Rect outFrame = new Rect();
final Rect outInsets = new Rect();
final Rect outStableInsets = new Rect();
@@ -1131,6 +1140,9 @@
assertFalse(app.hasFixedRotationTransform());
assertFalse(app2.hasFixedRotationTransform());
assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
+
+ mDisplayContent.finishFixedRotationAnimation();
+ assertNull(mDisplayContent.mFixedRotationAnimationController);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index d605ab2..27c4e9b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -27,9 +27,7 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
@@ -40,7 +38,6 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
@@ -50,7 +47,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
-import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import static org.testng.Assert.expectThrows;
@@ -64,18 +60,15 @@
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.InsetsState;
-import android.view.ViewRootImpl;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.WmDisplayCutout;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -116,12 +109,6 @@
updateDisplayFrames();
}
- @After
- public void tearDown() {
- PolicyControl.setFilters("");
- mWindow.getDisplayContent().mInputMethodTarget = null;
- }
-
public void setRotation(int rotation) {
mRotation = rotation;
updateDisplayFrames();
@@ -210,8 +197,6 @@
@Test
public void layoutWindowLw_fitStatusBars() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
addWindow(mWindow);
@@ -228,8 +213,6 @@
@Test
public void layoutWindowLw_fitNavigationBars() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
mWindow.mAttrs.setFitInsetsTypes(Type.navigationBars());
addWindow(mWindow);
@@ -246,8 +229,6 @@
@Test
public void layoutWindowLw_fitAllSides() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
mWindow.mAttrs.setFitInsetsSides(Side.all());
addWindow(mWindow);
@@ -264,8 +245,6 @@
@Test
public void layoutWindowLw_fitTopOnly() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
mWindow.mAttrs.setFitInsetsSides(Side.TOP);
addWindow(mWindow);
@@ -282,8 +261,6 @@
@Test
public void layoutWindowLw_fitInsetsIgnoringVisibility() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
final InsetsState state =
mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow);
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
@@ -304,8 +281,6 @@
@Test
public void layoutWindowLw_fitInsetsNotIgnoringVisibility() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
final InsetsState state =
mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow);
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
@@ -326,7 +301,6 @@
@Test
public void layoutWindowLw_fitDisplayCutout() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
addDisplayCutout();
mWindow.mAttrs.setFitInsetsTypes(Type.displayCutout());
@@ -683,31 +657,6 @@
}
@Test
- public void layoutWindowLw_withForwardInset_SoftInputAdjustResize() {
- assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_NONE);
-
- mWindow.mAttrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
- addWindow(mWindow);
-
- final int forwardedInsetBottom = 50;
- mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom));
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(),
- STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT + forwardedInsetBottom);
- assertInsetByTopBottom(mWindow.getVisibleFrameLw(),
- STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT + forwardedInsetBottom);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
-
- @Test
public void layoutWindowLw_withForwardInset_SoftInputAdjustNothing() {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
@@ -728,117 +677,6 @@
assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
}
- // TODO(b/118118435): remove after removing PolicyControl
- @FlakyTest(bugId = 129711077)
- @Test
- public void layoutWindowLw_withImmersive_SoftInputAdjustResize() {
- assumeTrue(ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- synchronized (mWm.mGlobalLock) {
- mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
- mWindow.mAttrs.flags = 0;
- mWindow.mAttrs.systemUiVisibility =
- SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
-
- addWindow(mWindow);
-
- mWindow.getDisplayContent().mInputMethodTarget = mWindow;
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP;
- mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP;
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- int bottomInset = mFrames.mDisplayHeight - INPUT_METHOD_WINDOW_TOP;
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), 0, 0);
- assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, bottomInset);
- }
- }
-
- // TODO(b/118118435): remove after removing PolicyControl
- @FlakyTest(bugId = 129711077)
- @Test
- public void layoutWindowLw_withImmersive_SoftInputAdjustNothing() {
- assumeTrue(ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- synchronized (mWm.mGlobalLock) {
- mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING;
- mWindow.mAttrs.flags = 0;
- mWindow.mAttrs.systemUiVisibility =
- SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
-
- addWindow(mWindow);
-
- mWindow.getDisplayContent().mInputMethodTarget = mWindow;
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP;
- mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP;
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), 0, 0);
- assertInsetByTopBottom(mWindow.getVisibleFrameLw(), 0, 0);
- }
- }
-
- // TODO(b/118118435): remove after removing PolicyControl
- @FlakyTest(bugId = 129711077)
- @Test
- public void layoutWindowLw_withForceImmersive_fullscreen() {
- assumeTrue(ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- synchronized (mWm.mGlobalLock) {
- mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
- mWindow.mAttrs.flags = 0;
- mWindow.mAttrs.systemUiVisibility = 0;
- PolicyControl.setFilters(PolicyControl.NAME_IMMERSIVE_FULL + "=*");
-
- addWindow(mWindow);
-
- mWindow.getDisplayContent().mInputMethodTarget = mWindow;
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP;
- mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP;
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- int bottomInset = mFrames.mDisplayHeight - INPUT_METHOD_WINDOW_TOP;
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), 0, 0);
- assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, bottomInset);
- }
- }
-
- // TODO(b/118118435): remove after removing PolicyControl
- @FlakyTest(bugId = 129711077)
- @Test
- public void layoutWindowLw_withForceImmersive_nonFullscreen() {
- assumeTrue(ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- synchronized (mWm.mGlobalLock) {
- mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
- mWindow.mAttrs.flags = 0;
- mWindow.mAttrs.systemUiVisibility = 0;
- mWindow.mAttrs.width = DISPLAY_WIDTH / 2;
- mWindow.mAttrs.height = DISPLAY_HEIGHT / 2;
- PolicyControl.setFilters(PolicyControl.NAME_IMMERSIVE_FULL + "=*");
-
- addWindow(mWindow);
-
- mWindow.getDisplayContent().mInputMethodTarget = mWindow;
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP;
- mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP;
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- int bottomInset = mFrames.mDisplayHeight - INPUT_METHOD_WINDOW_TOP;
- assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, bottomInset);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, bottomInset);
- assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, bottomInset);
- }
- }
-
@Test
public void layoutHint_appWindow() {
mWindow.mAttrs.flags =
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 552c476..79ba175 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -33,6 +33,7 @@
import static org.mockito.ArgumentMatchers.eq;
import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceControl.Transaction;
@@ -52,6 +53,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.PrintWriter;
+
/**
* Test class for {@link SurfaceAnimatorTest}.
*
@@ -267,6 +270,27 @@
assertFalse(mDeferFinishAnimatable.mFinishedCallbackCalled);
}
+ @Test
+ public void testDeferFinishFromAdapter() {
+
+ DeferredFinishAdapter deferredFinishAdapter = new DeferredFinishAdapter();
+ // Start animation
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, deferredFinishAdapter,
+ true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
+ assertAnimating(mAnimatable);
+ deferredFinishAdapter.mFinishCallback.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION,
+ deferredFinishAdapter);
+
+ assertAnimating(mAnimatable);
+ assertFalse(mAnimatable.mFinishedCallbackCalled);
+ // Now end defer finishing.
+ deferredFinishAdapter.mEndDeferFinishCallback.run();
+ assertNotAnimating(mAnimatable);
+ assertTrue(mAnimatable.mFinishedCallbackCalled);
+ verify(mTransaction).remove(eq(deferredFinishAdapter.mAnimationLeash));
+ }
+
private OnAnimationFinishedCallback startDeferFinishAnimatable(AnimationAdapter anim) {
mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, anim,
true /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
@@ -389,4 +413,51 @@
return true;
}
}
+
+ private static class DeferredFinishAdapter implements AnimationAdapter {
+
+ private Runnable mEndDeferFinishCallback;
+ private OnAnimationFinishedCallback mFinishCallback;
+ private SurfaceControl mAnimationLeash;
+
+ @Override
+ public boolean getShowWallpaper() {
+ return true;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, Transaction t, int type,
+ OnAnimationFinishedCallback finishCallback) {
+ mFinishCallback = finishCallback;
+ mAnimationLeash = animationLeash;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ }
+
+ @Override
+ public long getDurationHint() {
+ return 100;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return 100;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto) {
+ }
+
+ @Override
+ public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+ mEndDeferFinishCallback = endDeferFinishCallback;
+ return true;
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 20d9aff..f985a14 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -192,10 +192,18 @@
final ActivityManager.TaskSnapshot.Builder builder =
new ActivityManager.TaskSnapshot.Builder();
- mWm.mTaskSnapshotController.prepareTaskSnapshot(mAppWindow.mActivityRecord.getTask(),
- PixelFormat.UNKNOWN, builder);
+ boolean success = mWm.mTaskSnapshotController.prepareTaskSnapshot(
+ mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder);
+ assertTrue(success);
// The pixel format should be selected automatically.
assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat());
+
+ // Snapshot should not be taken while the rotation of activity and task are different.
+ doReturn(true).when(mAppWindow.mActivityRecord).hasFixedRotationTransform();
+ success = mWm.mTaskSnapshotController.prepareTaskSnapshot(
+ mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder);
+
+ assertFalse(success);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 65fb2c0..0346329 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -22,7 +22,6 @@
import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.Surface.ROTATION_0;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -75,17 +74,13 @@
import android.view.DisplayCutout;
import android.view.InsetsSource;
import android.view.SurfaceControl;
-import android.view.ViewRootImpl;
import android.view.WindowManager;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.wm.utils.WmDisplayCutout;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -103,21 +98,6 @@
@Presubmit
@RunWith(WindowTestRunner.class)
public class WindowStateTests extends WindowTestsBase {
- private static int sPreviousNewInsetsMode;
-
- @BeforeClass
- public static void setUpOnce() {
- // TODO: Make use of SettingsSession when it becomes feasible for this.
- sPreviousNewInsetsMode = ViewRootImpl.sNewInsetsMode;
- // To let the insets provider control the insets visibility, the insets mode has to be
- // NEW_INSETS_MODE_FULL.
- ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL;
- }
-
- @AfterClass
- public static void tearDownOnce() {
- ViewRootImpl.sNewInsetsMode = sPreviousNewInsetsMode;
- }
@Before
public void setUp() {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b59556f..060ed51 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -88,6 +88,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -815,6 +816,13 @@
} catch (IllegalArgumentException iae) {
Slog.e(TAG, "Failed to note usage start", iae);
}
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
+ mPackageManagerInternal.getPackageUid(event.mPackage, 0, userId),
+ event.mPackage,
+ event.mClass,
+ FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND);
break;
case Event.ACTIVITY_PAUSED:
if (event.mTaskRootPackage == null) {
@@ -829,6 +837,13 @@
event.mTaskRootClass = prevData.mTaskRootClass;
}
}
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
+ mPackageManagerInternal.getPackageUid(event.mPackage, 0, userId),
+ event.mPackage,
+ event.mClass,
+ FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND);
break;
case Event.ACTIVITY_DESTROYED:
// Treat activity destroys like activity stops.
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a28f281..50ff8f4 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1632,6 +1632,12 @@
"show_precise_failed_cause_bool";
/**
+ * Boolean to decide whether NR is enabled.
+ * @hide
+ */
+ public static final String KEY_NR_ENABLED_BOOL = "nr_enabled_bool";
+
+ /**
* Boolean to decide whether LTE is enabled.
*/
public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool";
@@ -4139,6 +4145,7 @@
sDefaults.putString(KEY_OPERATOR_NAME_FILTER_PATTERN_STRING, "");
sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, "");
sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true);
+ sDefaults.putBoolean(KEY_NR_ENABLED_BOOL, true);
sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false);
sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e85dfbe..4246aef 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10415,7 +10415,7 @@
* <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
* carrier with a canonical integer a.k.a. carrier id. The carrier ID is an Android
* platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
- * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
*
* <p>Apps which have carrier-specific configurations or business logic can use the carrier id
* as an Android platform-wide identifier for carriers.
@@ -10477,7 +10477,7 @@
*
* <p>For carriers without fine-grained specific carrier ids, return {@link #getSimCarrierId()}
* <p>Specific carrier ids are defined in the same way as carrier id
- * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
* except each with a "parent" id linking to its top-level carrier id.
*
* @return Returns fine-grained carrier id of the current subscription.
@@ -10526,7 +10526,7 @@
* This is used for fallback when configurations/logic for exact carrier id
* {@link #getSimCarrierId()} are not found.
*
- * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
* can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
* was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
* by default. After carrier id table update, a new carrier id was assigned. If apps don't
@@ -10553,7 +10553,7 @@
* used for fallback when configurations/logic for exact carrier id {@link #getSimCarrierId()}
* are not found.
*
- * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
* can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
* was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
* by default. After carrier id table update, a new carrier id was assigned. If apps don't
diff --git a/tests/NullHomeTest/Android.bp b/tests/NullHomeTest/Android.bp
new file mode 100644
index 0000000..99248bf
--- /dev/null
+++ b/tests/NullHomeTest/Android.bp
@@ -0,0 +1,22 @@
+// Copyright 2020 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "NullHomeTest",
+ srcs: ["src/**/*.java"],
+ certificate: "platform",
+ platform_apis: true,
+ static_libs: ["android-support-test"],
+ test_suites: ["device-tests"],
+}
diff --git a/tests/NullHomeTest/AndroidManifest.xml b/tests/NullHomeTest/AndroidManifest.xml
new file mode 100644
index 0000000..dc6402e
--- /dev/null
+++ b/tests/NullHomeTest/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.nullhome"
+ android:sharedUserId="android.uid.system" >
+
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.nullhome"
+ android:label="Check if no null Home exists/is enabled" />
+
+ <application android:label="Null Home Test">
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/tests/NullHomeTest/src/com/android/test/nullhome/NullHomeTest.java b/tests/NullHomeTest/src/com/android/test/nullhome/NullHomeTest.java
new file mode 100644
index 0000000..1d77cdc
--- /dev/null
+++ b/tests/NullHomeTest/src/com/android/test/nullhome/NullHomeTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.test.nullhome;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ * Check if NullHome/SystemUserHome activity does not exist/is disabled.
+ *
+ * SystemUserHome is only enabled in bootable CSI (csi_x86, csi_arm64)
+ * products and should not be enabled in other products.
+ *
+ * Shell's NullHome is empty and caused issues in sevaral manual GUI tests
+ * that try to select/use it, and should be removed.
+ *
+ * Settings' FallbackHome is fine because it's specially handled by Settings.
+ *
+ */
+
+@RunWith(JUnit4.class)
+public class NullHomeTest {
+ private static final String TAG = "NullHomeTest";
+ private Context mContext;
+ private PackageManager mPm;
+
+ @Before
+ public void before() {
+ Log.d(TAG, "beforeClass()");
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mPm = mContext.getPackageManager();
+ }
+
+ @Test
+ public void checkNullHome() {
+ final List<ResolveInfo> homeActivities = new ArrayList<>();
+
+ mPm.getHomeActivities(homeActivities);
+ for (ResolveInfo activity : homeActivities) {
+ Log.d(TAG, "Home activity: " + activity.activityInfo.packageName);
+ Assert.assertNotEquals(activity.activityInfo.packageName,
+ "com.android.internal.app.SystemUserHomeActivity");
+ Assert.assertNotEquals(activity.activityInfo.packageName,
+ "com.android.shell");
+ }
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java b/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java
index 2bf6040..e21dec3 100644
--- a/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java
@@ -15,27 +15,24 @@
*/
package com.android.test.uibench;
-import android.os.Bundle;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import androidx.appcompat.app.ActionBarDrawerToggle;
-import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.ListFragment;
+
+import com.android.test.uibench.listview.CompatListActivity;
import com.google.android.material.navigation.NavigationView;
-public class ClippedListActivity extends AppCompatActivity
+public class ClippedListActivity extends CompatListActivity
implements NavigationView.OnNavigationItemSelectedListener {
@Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ protected void initializeActivity() {
setContentView(R.layout.activity_navigation_drawer);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
@@ -48,15 +45,17 @@
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
+ }
- FragmentManager fm = getSupportFragmentManager();
- if (fm.findFragmentById(android.R.id.content) == null) {
- ListFragment listFragment = new ListFragment();
- ListAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
- TextUtils.buildSimpleStringList(40));
- listFragment.setListAdapter(adapter);
- fm.beginTransaction().add(R.id.app_bar_layout, listFragment).commit();
- }
+ @Override
+ protected ListAdapter createListAdapter() {
+ return new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
+ TextUtils.buildSimpleStringList(40));
+ }
+
+ @Override
+ protected int getListFragmentContainerViewId() {
+ return R.id.app_bar_layout;
}
@Override
diff --git a/tests/UiBench/src/com/android/test/uibench/MainActivity.java b/tests/UiBench/src/com/android/test/uibench/MainActivity.java
index 0a7aa42..77a6c32 100644
--- a/tests/UiBench/src/com/android/test/uibench/MainActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/MainActivity.java
@@ -19,13 +19,15 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.ListFragment;
-import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
+import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
+import androidx.fragment.app.ListFragment;
+
+import com.android.test.uibench.listview.CompatListActivity;
+
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
@@ -34,10 +36,12 @@
import java.util.List;
import java.util.Map;
-public class MainActivity extends AppCompatActivity {
+public class MainActivity extends CompatListActivity {
private static final String EXTRA_PATH = "activity_path";
private static final String CATEGORY_HWUI_TEST = "com.android.test.uibench.TEST";
+ private String mActivityPath = "";
+
public static class TestListFragment extends ListFragment {
@Override
@SuppressWarnings("unchecked")
@@ -56,9 +60,7 @@
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
+ protected void initializeActivity() {
Intent intent = getIntent();
String path = intent.getStringExtra(EXTRA_PATH);
@@ -68,15 +70,19 @@
// not root level, display where we are in the hierarchy
setTitle(path);
}
+ mActivityPath = path;
+ }
- FragmentManager fm = getSupportFragmentManager();
- if (fm.findFragmentById(android.R.id.content) == null) {
- ListFragment listFragment = new TestListFragment();
- listFragment.setListAdapter(new SimpleAdapter(this, getData(path),
- android.R.layout.simple_list_item_1, new String[] { "title" },
- new int[] { android.R.id.text1 }));
- fm.beginTransaction().add(android.R.id.content, listFragment).commit();
- }
+ @Override
+ protected ListAdapter createListAdapter() {
+ return new SimpleAdapter(this, getData(mActivityPath),
+ android.R.layout.simple_list_item_1, new String[] { "title" },
+ new int[] { android.R.id.text1 });
+ }
+
+ @Override
+ protected ListFragment createListFragment() {
+ return new TestListFragment();
}
protected List<Map<String, Object>> getData(String prefix) {
diff --git a/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java b/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java
index af7c65a..d6e1d06 100644
--- a/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java
@@ -16,13 +16,15 @@
package com.android.test.uibench;
import android.os.Bundle;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.ListFragment;
-import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
-public class ShadowGridActivity extends AppCompatActivity {
+import androidx.fragment.app.ListFragment;
+
+import com.android.test.uibench.listview.CompatListActivity;
+
+public class ShadowGridActivity extends CompatListActivity {
public static class NoDividerListFragment extends ListFragment {
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -31,18 +33,14 @@
}
};
+ @Override
+ protected ListAdapter createListAdapter() {
+ return new ArrayAdapter<>(this, R.layout.card_row, R.id.card_text,
+ TextUtils.buildSimpleStringList());
+ }
@Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- FragmentManager fm = getSupportFragmentManager();
- if (fm.findFragmentById(android.R.id.content) == null) {
- ListFragment listFragment = new NoDividerListFragment();
-
- listFragment.setListAdapter(new ArrayAdapter<>(this,
- R.layout.card_row, R.id.card_text, TextUtils.buildSimpleStringList()));
- fm.beginTransaction().add(android.R.id.content, listFragment).commit();
- }
+ protected ListFragment createListFragment() {
+ return new NoDividerListFragment();
}
}
diff --git a/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java b/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
index 6659558..9a4b527 100644
--- a/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
@@ -16,22 +16,29 @@
package com.android.test.uibench.listview;
import android.os.Bundle;
+import android.widget.ListAdapter;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.ListFragment;
-import androidx.appcompat.app.AppCompatActivity;
-import android.widget.ListAdapter;
public abstract class CompatListActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ initializeActivity();
+ int containerViewId = getListFragmentContainerViewId();
FragmentManager fm = getSupportFragmentManager();
- if (fm.findFragmentById(android.R.id.content) == null) {
+ Fragment fragment = fm.findFragmentById(containerViewId);
+ if (fragment == null) {
ListFragment listFragment = createListFragment();
listFragment.setListAdapter(createListAdapter());
- fm.beginTransaction().add(android.R.id.content, listFragment).commit();
+ fm.beginTransaction().add(containerViewId, listFragment).commit();
+ } else if (fragment instanceof ListFragment) {
+ ((ListFragment) fragment).setListAdapter(createListAdapter());
}
}
@@ -40,4 +47,11 @@
protected ListFragment createListFragment() {
return new ListFragment();
}
+
+ protected int getListFragmentContainerViewId() {
+ return android.R.id.content;
+ }
+
+ protected void initializeActivity() {
+ }
}
diff --git a/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java
index bd313ad..2f0d6ab 100644
--- a/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java
@@ -17,16 +17,17 @@
import android.content.Context;
import android.os.Bundle;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.android.test.uibench.R;
public abstract class RvCompatListActivity extends AppCompatActivity {
@@ -51,14 +52,21 @@
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
- if (fm.findFragmentById(android.R.id.content) == null) {
+ Fragment existingFragment = fm.findFragmentById(android.R.id.content);
+ if (existingFragment == null) {
RecyclerViewFragment fragment = new RecyclerViewFragment();
- fragment.layoutManager = createLayoutManager(this);
- fragment.adapter = createAdapter();
+ initializeRecyclerViewFragment(fragment);
fm.beginTransaction().add(android.R.id.content, fragment).commit();
+ } else if (existingFragment instanceof RecyclerViewFragment) {
+ initializeRecyclerViewFragment((RecyclerViewFragment) existingFragment);
}
}
+ private void initializeRecyclerViewFragment(RecyclerViewFragment fragment) {
+ fragment.layoutManager = createLayoutManager(this);
+ fragment.adapter = createAdapter();
+ }
+
protected RecyclerView.LayoutManager createLayoutManager(Context context) {
return new LinearLayoutManager(context);
}
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
index eec3cdbe..8c2de40 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -52,7 +52,7 @@
doReturn(mock(IBinder::class.java)).`when`(it).getSystemService(Context.NETD_SERVICE)
}
- private class TestPermissionChecker : NetworkStackConnector.PermissionChecker() {
+ private class TestPermissionChecker : NetworkStackService.PermissionChecker() {
override fun enforceNetworkStackCallingPermission() = Unit
}
@@ -62,8 +62,8 @@
override fun sendNetworkConditionsBroadcast(context: Context, broadcast: Intent) = Unit
}
- private inner class TestNetworkStackConnector(context: Context) :
- NetworkStackConnector(context, TestPermissionChecker()) {
+ private inner class TestNetworkStackConnector(context: Context) : NetworkStackConnector(
+ context, TestPermissionChecker(), NetworkStackService.Dependencies()) {
private val network = Network(TEST_NETID)
private val privateDnsBypassNetwork = TestNetwork(TEST_NETID)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a992778..d2b26d3 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -75,6 +75,7 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType;
@@ -6945,7 +6946,7 @@
final NetworkAgentInfo naiWithoutUid =
new NetworkAgentInfo(
null, null, null, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0);
+ mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
mServiceContext.setPermission(
android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
@@ -6961,7 +6962,7 @@
final NetworkAgentInfo naiWithoutUid =
new NetworkAgentInfo(
null, null, null, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0);
+ mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
@@ -6977,7 +6978,7 @@
final NetworkAgentInfo naiWithoutUid =
new NetworkAgentInfo(
null, null, null, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0);
+ mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
@@ -6994,7 +6995,7 @@
final NetworkAgentInfo naiWithoutUid =
new NetworkAgentInfo(
null, null, network, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0);
+ mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
@@ -7028,7 +7029,7 @@
final NetworkAgentInfo naiWithUid =
new NetworkAgentInfo(
null, null, null, null, null, nc, 0, mServiceContext, null, null,
- mService, null, null, null, 0);
+ mService, null, null, null, 0, INVALID_UID);
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
@@ -7050,7 +7051,7 @@
final NetworkAgentInfo naiWithUid =
new NetworkAgentInfo(
null, null, null, null, null, nc, 0, mServiceContext, null, null,
- mService, null, null, null, 0);
+ mService, null, null, null, 0, INVALID_UID);
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 24a8717..aafa18a 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -38,6 +38,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkProvider;
+import android.os.Binder;
import android.os.INetworkManagementService;
import android.text.format.DateUtils;
@@ -354,7 +355,7 @@
caps.addTransportType(transport);
NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
caps, 50, mCtx, null, null /* config */, mConnService, mNetd, mDnsResolver, mNMS,
- NetworkProvider.ID_NONE);
+ NetworkProvider.ID_NONE, Binder.getCallingUid());
nai.everValidated = true;
return nai;
}