summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--apex/statsd/Android.bp1
-rw-r--r--apex/statsd/tests/libstatspull/Android.bp2
-rw-r--r--api/test-current.txt2
-rw-r--r--cmds/statsd/Android.bp6
-rw-r--r--core/java/android/content/pm/FileSystemControlParcel.aidl3
-rw-r--r--core/java/android/os/Process.java12
-rw-r--r--core/java/android/os/ZygoteProcess.java38
-rw-r--r--core/java/android/os/incremental/IIncrementalService.aidl7
-rw-r--r--core/java/android/os/incremental/IIncrementalServiceConnector.aidl27
-rw-r--r--core/java/android/os/incremental/IncrementalManager.java19
-rw-r--r--core/java/android/service/dataloader/DataLoaderService.java22
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java8
-rw-r--r--core/java/android/view/InsetsSourceControl.java3
-rw-r--r--core/java/android/view/SurfaceControl.java38
-rw-r--r--core/java/com/android/internal/os/Zygote.java36
-rw-r--r--core/java/com/android/internal/os/ZygoteArguments.java15
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java3
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java8
-rw-r--r--core/jni/android_view_SurfaceControl.cpp8
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp120
-rw-r--r--core/proto/android/stats/dnsresolver/dns_resolver.proto21
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java3
-rw-r--r--packages/SystemUI/res/layout/bubble_dismiss_target.xml2
-rw-r--r--packages/SystemUI/res/layout/pip_dismiss_view.xml35
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/Interpolators.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java188
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java149
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java253
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt36
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt8
-rw-r--r--packages/Tethering/tests/integration/Android.bp5
-rw-r--r--packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java2
-rw-r--r--packages/Tethering/tests/unit/AndroidManifest.xml9
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java56
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java194
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java14
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java7
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java131
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java419
-rw-r--r--services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java403
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java36
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java70
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java3
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java105
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java4
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java20
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java211
-rw-r--r--services/core/java/com/android/server/pm/Installer.java24
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java2
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java6
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java8
-rw-r--r--services/core/java/com/android/server/wm/ShellRoot.java32
-rw-r--r--services/core/java/com/android/server/wm/Task.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java28
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java4
-rw-r--r--services/incremental/BinderIncrementalService.cpp5
-rw-r--r--services/incremental/BinderIncrementalService.h1
-rw-r--r--services/incremental/IncrementalService.cpp8
-rw-r--r--services/incremental/IncrementalService.h16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java15
-rw-r--r--telephony/common/android/telephony/LocationAccessPolicy.java2
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java16
-rw-r--r--telephony/java/com/android/internal/telephony/DctConstants.java1
-rw-r--r--tests/net/common/java/android/net/DependenciesTest.java113
-rw-r--r--tests/net/common/java/android/net/NetworkCapabilitiesTest.java247
-rw-r--r--tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt124
-rw-r--r--tools/stats_log_api_gen/Collation.h2
92 files changed, 2659 insertions, 1192 deletions
diff --git a/Android.bp b/Android.bp
index 874d76fe8d00..d4ca7066781a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -911,6 +911,7 @@ cc_library {
filegroup {
name: "incremental_aidl",
srcs: [
+ "core/java/android/os/incremental/IIncrementalServiceConnector.aidl",
"core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl",
],
path: "core/java",
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index 32e13e31eebe..15d74951019d 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -67,7 +67,6 @@ cc_library_shared {
"liblog", // Has a stable abi - should not be copied into apex.
"libstatssocket",
],
- //TODO: is libc++_static correct?
stl: "libc++_static",
cflags: [
"-Wall",
diff --git a/apex/statsd/tests/libstatspull/Android.bp b/apex/statsd/tests/libstatspull/Android.bp
index 2d64f190839c..0df96e149d4f 100644
--- a/apex/statsd/tests/libstatspull/Android.bp
+++ b/apex/statsd/tests/libstatspull/Android.bp
@@ -32,7 +32,7 @@ android_test {
"protos/**/*.proto",
],
test_suites: [
- "general-tests",
+ "device-tests",
],
platform_apis: true,
privileged: true,
diff --git a/api/test-current.txt b/api/test-current.txt
index 5de6c2ae4c82..c29f1c4c657d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4860,7 +4860,9 @@ package android.view {
}
public final class SurfaceControl implements android.os.Parcelable {
+ ctor public SurfaceControl(@NonNull android.view.SurfaceControl);
method public static long acquireFrameRateFlexibilityToken();
+ method public boolean isSameSurface(@NonNull android.view.SurfaceControl);
method public static void releaseFrameRateFlexibilityToken(long);
}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index d3d7e1d483e8..65061d0c9bda 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -113,17 +113,18 @@ cc_defaults {
static_libs: [
"libbase",
"libcutils",
+ "libgtest_prod",
"libprotoutil",
"libstatsmetadata",
"libstatslog_statsd",
"libsysutils",
"libutils",
+ "statsd-aidl-ndk_platform",
],
shared_libs: [
"libbinder_ndk",
"libincident",
"liblog",
- "statsd-aidl-ndk_platform",
],
}
@@ -268,10 +269,11 @@ cc_binary {
proto: {
type: "lite",
+ static: true,
},
+ stl: "libc++_static",
shared_libs: [
- "libgtest_prod",
"libstatssocket",
],
diff --git a/core/java/android/content/pm/FileSystemControlParcel.aidl b/core/java/android/content/pm/FileSystemControlParcel.aidl
index f00feaeb2f5a..92df16ced8a3 100644
--- a/core/java/android/content/pm/FileSystemControlParcel.aidl
+++ b/core/java/android/content/pm/FileSystemControlParcel.aidl
@@ -17,6 +17,7 @@
package android.content.pm;
import android.content.pm.IPackageInstallerSessionFileSystemConnector;
+import android.os.incremental.IIncrementalServiceConnector;
import android.os.incremental.IncrementalFileSystemControlParcel;
/**
@@ -26,6 +27,8 @@ import android.os.incremental.IncrementalFileSystemControlParcel;
parcelable FileSystemControlParcel {
// Incremental FS control descriptors.
@nullable IncrementalFileSystemControlParcel incremental;
+ // Incremental FS service.
+ @nullable IIncrementalServiceConnector service;
// Callback-based installation connector.
@nullable IPackageInstallerSessionFileSystemConnector callback;
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b7b3c4fc8add..5d2c9d18c00c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -607,6 +607,9 @@ public class Process {
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
+ * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+ * volume UUID and inode number.
+ * @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
* @param zygoteArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
@@ -631,13 +634,17 @@ public class Process {
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ @Nullable Map<String, Pair<String, Long>>
+ whitelistedDataInfoMap,
+ boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] zygoteArgs) {
return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
zygotePolicyFlags, isTopApp, disabledCompatChanges,
- pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
+ pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+ bindMountAppStorageDirs, zygoteArgs);
}
/** @hide */
@@ -661,7 +668,8 @@ public class Process {
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
- disabledCompatChanges, /* pkgDataInfoMap */ null, false, zygoteArgs);
+ disabledCompatChanges, /* pkgDataInfoMap */ null,
+ /* whitelistedDataInfoMap */ null, false, false, zygoteArgs);
}
/**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 5f3f14facd75..a4c99c006d80 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,6 +333,9 @@ public class ZygoteProcess {
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
+ * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+ * volume UUID and inode number.
+ * @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
*
* @param zygoteArgs Additional arguments to supply to the Zygote process.
@@ -355,6 +358,9 @@ public class ZygoteProcess {
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ @Nullable Map<String, Pair<String, Long>>
+ whitelistedDataInfoMap,
+ boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] zygoteArgs) {
// TODO (chriswailes): Is there a better place to check this value?
@@ -367,7 +373,8 @@ public class ZygoteProcess {
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
- pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
+ pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+ bindMountAppStorageDirs, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -608,6 +615,9 @@ public class ZygoteProcess {
* @param disabledCompatChanges a list of disabled compat changes for the process being started.
* @param pkgDataInfoMap Map from related package names to private data directory volume UUID
* and inode number.
+ * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+ * volume UUID and inode number.
+ * @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
@@ -631,6 +641,9 @@ public class ZygoteProcess {
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ @Nullable Map<String, Pair<String, Long>>
+ whitelistedDataInfoMap,
+ boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
@@ -728,11 +741,33 @@ public class ZygoteProcess {
}
argsForZygote.add(sb.toString());
}
+ if (whitelistedDataInfoMap != null && whitelistedDataInfoMap.size() > 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Zygote.WHITELISTED_DATA_INFO_MAP);
+ sb.append("=");
+ boolean started = false;
+ for (Map.Entry<String, Pair<String, Long>> entry : whitelistedDataInfoMap.entrySet()) {
+ if (started) {
+ sb.append(',');
+ }
+ started = true;
+ sb.append(entry.getKey());
+ sb.append(',');
+ sb.append(entry.getValue().first);
+ sb.append(',');
+ sb.append(entry.getValue().second);
+ }
+ argsForZygote.add(sb.toString());
+ }
if (bindMountAppStorageDirs) {
argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
}
+ if (bindMountAppsData) {
+ argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS);
+ }
+
if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--disabled-compat-changes=");
@@ -1291,6 +1326,7 @@ public class ZygoteProcess {
true /* startChildZygote */, null /* packageName */,
ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
+ null /* whitelistedDataInfoMap */, false /* bindMountAppsData*/,
/* bindMountAppStorageDirs */ false, extraArgs);
} catch (ZygoteStartFailedEx ex) {
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index d8308c7c3362..2dbaea860e2a 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -38,13 +38,6 @@ interface IIncrementalService {
int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);
/**
- * Changes storage params. Returns 0 on success, and -errno on failure.
- * Use enableReadLogs to switch pages read logs reporting on and off.
- * Returns 0 on success, and - errno on failure: permission check or remount.
- */
- int setStorageParams(int storageId, boolean enableReadLogs);
-
- /**
* Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
*/
const int BIND_TEMPORARY = 0;
diff --git a/core/java/android/os/incremental/IIncrementalServiceConnector.aidl b/core/java/android/os/incremental/IIncrementalServiceConnector.aidl
new file mode 100644
index 000000000000..5800ecf63a1e
--- /dev/null
+++ b/core/java/android/os/incremental/IIncrementalServiceConnector.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.incremental;
+
+/** @hide */
+interface IIncrementalServiceConnector {
+ /**
+ * Changes storage params. Returns 0 on success, and -errno on failure.
+ * Use enableReadLogs to switch pages read logs reporting on and off.
+ * Returns 0 on success, and - errno on failure: permission check or remount.
+ */
+ int setStorageParams(boolean enableReadLogs);
+}
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 5f01408944e8..35518db32829 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -19,13 +19,11 @@ package android.os.incremental;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
import android.os.RemoteException;
-import android.system.ErrnoException;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -321,23 +319,6 @@ public final class IncrementalManager {
return nativeUnsafeGetFileSignature(path);
}
- /**
- * Sets storage parameters.
- *
- * @param enableReadLogs - enables or disables read logs. Caller has to have a permission.
- */
- @RequiresPermission(android.Manifest.permission.LOADER_USAGE_STATS)
- public void setStorageParams(int storageId, boolean enableReadLogs) throws ErrnoException {
- try {
- int res = mService.setStorageParams(storageId, enableReadLogs);
- if (res < 0) {
- throw new ErrnoException("setStorageParams", -res);
- }
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
-
/* Native methods */
private static native boolean nativeIsEnabled();
private static native boolean nativeIsIncrementalPath(@NonNull String path);
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 05877a59368a..c047dc0d07c7 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.Service;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.DataLoaderParams;
import android.content.pm.DataLoaderParamsParcel;
@@ -32,8 +31,6 @@ import android.content.pm.InstallationFile;
import android.content.pm.InstallationFileParcel;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
-import android.os.incremental.IncrementalManager;
-import android.system.ErrnoException;
import android.util.ExceptionUtils;
import android.util.Slog;
@@ -211,25 +208,6 @@ public abstract class DataLoaderService extends Service {
private final long mNativeInstance;
}
- /* Used by native FileSystemConnector. */
- private boolean setStorageParams(int storageId, boolean enableReadLogs) {
- IncrementalManager incrementalManager = (IncrementalManager) getSystemService(
- Context.INCREMENTAL_SERVICE);
- if (incrementalManager == null) {
- Slog.e(TAG, "Failed to obtain incrementalManager: " + storageId);
- return false;
- }
- try {
- // This has to be done directly in incrementalManager as the storage
- // might be missing still.
- incrementalManager.setStorageParams(storageId, enableReadLogs);
- } catch (ErrnoException e) {
- Slog.e(TAG, "Failed to set params for storage: " + storageId, e);
- return false;
- }
- return true;
- }
-
/* Native methods */
private native boolean nativeCreateDataLoader(int storageId,
@NonNull FileSystemControlParcel control,
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 69bab4df2cae..6cb93746a9a4 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -91,6 +91,8 @@ public class InsetsSourceConsumer {
if (mSourceControl == control) {
return;
}
+ SurfaceControl oldLeash = mSourceControl != null ? mSourceControl.getLeash() : null;
+
final InsetsSourceControl lastControl = mSourceControl;
mSourceControl = control;
@@ -116,6 +118,12 @@ public class InsetsSourceConsumer {
// However make sure that the leash visibility is still up to date.
if (applyLocalVisibilityOverride()) {
mController.notifyVisibilityChanged();
+ }
+
+ // If we have a new leash, make sure visibility is up-to-date, even though we
+ // didn't want to run an animation above.
+ SurfaceControl newLeash = mSourceControl.getLeash();
+ if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) {
applyHiddenToControl();
}
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index f3ec65f997ba..e001b668f71a 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -44,8 +44,7 @@ public class InsetsSourceControl implements Parcelable {
public InsetsSourceControl(InsetsSourceControl other) {
mType = other.mType;
if (other.mLeash != null) {
- mLeash = new SurfaceControl();
- mLeash.copyFrom(other.mLeash);
+ mLeash = new SurfaceControl(other.mLeash);
} else {
mLeash = null;
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b5f9df72f756..ab65e3a5c849 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -216,6 +216,7 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeSetFrameRate(
long transactionObj, long nativeObject, float frameRate, int compatibility);
+ private static native long nativeGetHandle(long nativeObject);
private static native long nativeAcquireFrameRateFlexibilityToken();
private static native void nativeReleaseFrameRateFlexibilityToken(long token);
@@ -226,6 +227,7 @@ public final class SurfaceControl implements Parcelable {
* @hide
*/
public long mNativeObject;
+ private long mNativeHandle;
// TODO: Move this to native.
private final Object mSizeLock = new Object();
@@ -428,12 +430,13 @@ public final class SurfaceControl implements Parcelable {
mCloseGuard.open("release");
}
mNativeObject = nativeObject;
+ mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
}
/**
* @hide
*/
- public void copyFrom(SurfaceControl other) {
+ public void copyFrom(@NonNull SurfaceControl other) {
mName = other.mName;
mWidth = other.mWidth;
mHeight = other.mHeight;
@@ -853,23 +856,19 @@ public final class SurfaceControl implements Parcelable {
throw new OutOfResourcesException(
"Couldn't allocate SurfaceControl native object");
}
-
+ mNativeHandle = nativeGetHandle(mNativeObject);
mCloseGuard.open("release");
}
- /** This is a transfer constructor, useful for transferring a live SurfaceControl native
- * object to another Java wrapper which could have some different behavior, e.g.
- * event logging.
+ /**
+ * Copy constructor. Creates a new native object pointing to the same surface as {@code other}.
+ *
+ * @param other The object to copy the surface from.
* @hide
*/
- public SurfaceControl(SurfaceControl other) {
- mName = other.mName;
- mWidth = other.mWidth;
- mHeight = other.mHeight;
- mNativeObject = other.mNativeObject;
- other.mCloseGuard.close();
- other.mNativeObject = 0;
- mCloseGuard.open("release");
+ @TestApi
+ public SurfaceControl(@NonNull SurfaceControl other) {
+ copyFrom(other);
}
private SurfaceControl(Parcel in) {
@@ -921,6 +920,18 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Checks whether two {@link SurfaceControl} objects represent the same surface.
+ *
+ * @param other The other object to check
+ * @return {@code true} if these two {@link SurfaceControl} objects represent the same surface.
+ * @hide
+ */
+ @TestApi
+ public boolean isSameSurface(@NonNull SurfaceControl other) {
+ return other.mNativeHandle == mNativeHandle;
+ }
+
+ /**
* Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
* android.view.SurfaceControlProto}.
*
@@ -977,6 +988,7 @@ public final class SurfaceControl implements Parcelable {
if (mNativeObject != 0) {
nativeRelease(mNativeObject);
mNativeObject = 0;
+ mNativeHandle = 0;
mCloseGuard.close();
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index ff03f1a1a2ab..505a05eb9c23 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -205,9 +205,15 @@ public final class Zygote {
/** List of packages with the same uid, and its app data info: volume uuid and inode. */
public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
+ /** List of whitelisted packages and its app data info: volume uuid and inode. */
+ public static final String WHITELISTED_DATA_INFO_MAP = "--whitelisted-data-info-map";
+
/** Bind mount app storage dirs to lower fs not via fuse */
public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs";
+ /** Bind mount app storage dirs to lower fs not via fuse */
+ public static final String BIND_MOUNT_APP_DATA_DIRS = "--bind-mount-data-dirs";
+
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying a name
* in the abstract socket namespace. This socket name is what the new child zygote
@@ -313,6 +319,8 @@ public final class Zygote {
* @param isTopApp true if the process is for top (high priority) application.
* @param pkgDataInfoList A list that stores related packages and its app data
* info: volume uuid and inode.
+ * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps.
+ * @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*
* @return 0 if this is the child, pid of the child
@@ -321,13 +329,15 @@ public final class Zygote {
static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- boolean isTopApp, String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
+ boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+ boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
- pkgDataInfoList, bindMountAppStorageDirs);
+ pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs,
+ bindMountAppStorageDirs);
if (pid == 0) {
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -344,6 +354,7 @@ public final class Zygote {
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
+ String[] whitelistedDataInfoList, boolean bindMountAppDataDirs,
boolean bindMountAppStorageDirs);
/**
@@ -371,15 +382,19 @@ public final class Zygote {
* volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
* app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
* app_b_ce_inode, ...];
+ * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps.
+ * @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*/
private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
- String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
+ String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+ boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
- pkgDataInfoList, bindMountAppStorageDirs);
+ pkgDataInfoList, whitelistedDataInfoList,
+ bindMountAppDataDirs, bindMountAppStorageDirs);
// Note that this event ends at the end of handleChildProc.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -399,7 +414,8 @@ public final class Zygote {
private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
- String[] pkgDataInfoList, boolean bindMountAppStorageDirs);
+ String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+ boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs);
/**
* Called to do any initialization before starting an application.
@@ -724,7 +740,8 @@ public final class Zygote {
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
- args.mPkgDataInfoList, args.mBindMountAppStorageDirs);
+ args.mPkgDataInfoList, args.mWhitelistedDataInfoList,
+ args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -1060,4 +1077,11 @@ public final class Zygote {
*/
@FastNative
public static native int nativeParseSigChld(byte[] in, int length, int[] out);
+
+ /**
+ * Returns whether the kernel supports tagged pointers. Present in the
+ * Android Common Kernel from 4.14 and up. By default, you should prefer
+ * fully-feature Memory Tagging, rather than the static Tagged Pointers.
+ */
+ public static native boolean nativeSupportsTaggedPointers();
}
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 1a63765fcaa6..94c1f71a26db 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -227,11 +227,22 @@ class ZygoteArguments {
String[] mPkgDataInfoList;
/**
+ * A list that stores all whitelisted app data info: volume uuid and inode.
+ * Null if it does need to do app data isolation.
+ */
+ String[] mWhitelistedDataInfoList;
+
+ /**
* @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS
*/
boolean mBindMountAppStorageDirs;
/**
+ * @see Zygote#BIND_MOUNT_APP_DATA_DIRS
+ */
+ boolean mBindMountAppDataDirs;
+
+ /**
* Constructs instance and parses args
*
* @param args zygote command-line args
@@ -452,8 +463,12 @@ class ZygoteArguments {
}
} else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) {
mPkgDataInfoList = getAssignmentList(arg);
+ } else if (arg.startsWith(Zygote.WHITELISTED_DATA_INFO_MAP)) {
+ mWhitelistedDataInfoList = getAssignmentList(arg);
} else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) {
mBindMountAppStorageDirs = true;
+ } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) {
+ mBindMountAppDataDirs = true;
} else {
break;
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index bc8dfd4aa402..e6a3029c5b2b 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -258,7 +258,8 @@ class ZygoteConnection {
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
- parsedArgs.mPkgDataInfoList, parsedArgs.mBindMountAppStorageDirs);
+ parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
+ parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
try {
if (pid == 0) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index ec1f516df5f3..c2b13c971020 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -757,9 +757,11 @@ public class ZygoteInit {
Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);
- /* Enable pointer tagging in the system server unconditionally. Hardware support for
- * this is present in all ARMv8 CPUs; this flag has no effect on other platforms. */
- parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ if (Zygote.nativeSupportsTaggedPointers()) {
+ /* Enable pointer tagging in the system server. Hardware support for this is present
+ * in all ARMv8 CPUs. */
+ parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ }
/* Enable gwp-asan on the system server with a small probability. This is the same
* policy as applied to native processes and system apps. */
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b11c4c92ff33..1cfa12df32ab 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1430,6 +1430,12 @@ static void nativeSetGlobalShadowSettings(JNIEnv* env, jclass clazz, jfloatArray
client->setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, lightRadius);
}
+
+static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
+ SurfaceControl *surfaceControl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ return reinterpret_cast<jlong>(surfaceControl->getHandle().get());
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -1606,6 +1612,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeMirrorSurface },
{"nativeSetGlobalShadowSettings", "([F[FFFF)V",
(void*)nativeSetGlobalShadowSettings },
+ {"nativeGetHandle", "(J)J",
+ (void*)nativeGetHandle },
};
int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index ea3c0fa9fc3c..4b30359e671a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -110,7 +110,6 @@ using android::base::StringAppendF;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
using android::base::GetBoolProperty;
-using android::base::GetProperty;
#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
append(StringPrintf(__VA_ARGS__))
@@ -170,18 +169,6 @@ static int gSystemServerSocketFd = -1;
static constexpr int DEFAULT_DATA_DIR_PERMISSION = 0751;
-/**
- * Property to control if app data isolation is enabled.
- */
-static const std::string ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY =
- "persist.zygote.app_data_isolation";
-
-/**
- * Property to enable app data isolation for sdcard obb or data in vold.
- */
-static const std::string ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
- "persist.sys.vold_app_data_isolation_enabled";
-
static constexpr const uint64_t UPPER_HALF_WORD_MASK = 0xFFFF'FFFF'0000'0000;
static constexpr const uint64_t LOWER_HALF_WORD_MASK = 0x0000'0000'FFFF'FFFF;
@@ -1319,20 +1306,13 @@ static void relabelAllDirs(const char* path, security_context_t context, fail_fn
* be decrypted after storage is decrypted.
*
*/
-static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
- uid_t uid, const char* process_name, jstring managed_nice_name,
- fail_fn_t fail_fn) {
+static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_data_info_list,
+ uid_t uid, const char* process_name,
+ jstring managed_nice_name, fail_fn_t fail_fn) {
const userid_t userId = multiuser_get_user_id(uid);
- auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
-
- int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
- // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode>
- if ((size % 3) != 0) {
- fail_fn(CREATE_ERROR("Wrong pkg_inode_list size %d", size));
- }
- ensureInAppMountNamespace(fail_fn);
+ int size = merged_data_info_list.size();
// Mount tmpfs on all possible data directories, so app no longer see the original apps data.
char internalCePath[PATH_MAX];
@@ -1377,14 +1357,10 @@ static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
bool legacySymlinkCreated = false;
for (int i = 0; i < size; i += 3) {
- jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
- std::string packageName = extract_fn(package_str).value();
+ std::string const & packageName = merged_data_info_list[i];
+ std::string const & volUuid = merged_data_info_list[i + 1];
+ std::string const & inode = merged_data_info_list[i + 2];
- jstring vol_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 1));
- std::string volUuid = extract_fn(vol_str).value();
-
- jstring inode_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 2));
- std::string inode = extract_fn(inode_str).value();
std::string::size_type sz;
long long ceDataInode = std::stoll(inode, &sz);
@@ -1482,6 +1458,48 @@ static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
freecon(dataDataContext);
}
+static void insertPackagesToMergedList(JNIEnv* env,
+ std::vector<std::string>& merged_data_info_list,
+ jobjectArray data_info_list, const char* process_name,
+ jstring managed_nice_name, fail_fn_t fail_fn) {
+
+ auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
+
+ int size = (data_info_list != nullptr) ? env->GetArrayLength(data_info_list) : 0;
+ // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode>
+ if ((size % 3) != 0) {
+ fail_fn(CREATE_ERROR("Wrong data_info_list size %d", size));
+ }
+
+ for (int i = 0; i < size; i += 3) {
+ jstring package_str = (jstring) (env->GetObjectArrayElement(data_info_list, i));
+ std::string packageName = extract_fn(package_str).value();
+ merged_data_info_list.push_back(packageName);
+
+ jstring vol_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 1));
+ std::string volUuid = extract_fn(vol_str).value();
+ merged_data_info_list.push_back(volUuid);
+
+ jstring inode_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 2));
+ std::string inode = extract_fn(inode_str).value();
+ merged_data_info_list.push_back(inode);
+ }
+}
+
+static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
+ jobjectArray whitelisted_data_info_list, uid_t uid, const char* process_name,
+ jstring managed_nice_name, fail_fn_t fail_fn) {
+
+ ensureInAppMountNamespace(fail_fn);
+ std::vector<std::string> merged_data_info_list;
+ insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list,
+ process_name, managed_nice_name, fail_fn);
+ insertPackagesToMergedList(env, merged_data_info_list, whitelisted_data_info_list,
+ process_name, managed_nice_name, fail_fn);
+
+ isolateAppData(env, merged_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+}
+
/**
* Like isolateAppData(), isolate jit profile directories, so apps don't see what
* other apps are installed by reading content inside /data/misc/profiles/cur.
@@ -1594,7 +1612,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jstring managed_nice_name, bool is_system_server,
bool is_child_zygote, jstring managed_instruction_set,
jstring managed_app_data_dir, bool is_top_app,
- jobjectArray pkg_data_info_list, bool mount_storage_dirs) {
+ jobjectArray pkg_data_info_list,
+ jobjectArray whitelisted_data_info_list,
+ bool mount_data_dirs, bool mount_storage_dirs) {
const char* process_name = is_system_server ? "system_server" : "zygote";
auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
@@ -1628,9 +1648,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
// give a null in same_uid_pkgs and private_volumes so they don't need app data isolation.
// Isolated process / webview / app zygote should be gated by SELinux and file permission
// so they can't even traverse CE / DE directories.
- if (pkg_data_info_list != nullptr
- && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
- isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+ if (mount_data_dirs) {
+ isolateAppData(env, pkg_data_info_list, whitelisted_data_info_list,
+ uid, process_name, managed_nice_name, fail_fn);
isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) {
@@ -2003,7 +2023,8 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
jint mount_external, jstring se_info, jstring nice_name,
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
- jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
+ jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
+ jboolean mount_data_dirs, jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -2041,6 +2062,8 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
is_top_app == JNI_TRUE, pkg_data_info_list,
+ whitelisted_data_info_list,
+ mount_data_dirs == JNI_TRUE,
mount_storage_dirs == JNI_TRUE);
}
return pid;
@@ -2076,7 +2099,8 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
permitted_capabilities, effective_capabilities,
MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
false, nullptr, nullptr, /* is_top_app= */ false,
- /* pkg_data_info_list */ nullptr, false);
+ /* pkg_data_info_list */ nullptr,
+ /* whitelisted_data_info_list */ nullptr, false, false);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -2206,15 +2230,16 @@ static void com_android_internal_os_Zygote_nativeSpecializeAppProcess(
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
- jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
+ jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
+ jboolean mount_data_dirs, jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
- is_top_app == JNI_TRUE, pkg_data_info_list,
- mount_storage_dirs == JNI_TRUE);
+ is_top_app == JNI_TRUE, pkg_data_info_list, whitelisted_data_info_list,
+ mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE);
}
/**
@@ -2405,10 +2430,19 @@ static jint com_android_internal_os_Zygote_nativeParseSigChld(JNIEnv* env, jclas
return -1;
}
+static jboolean com_android_internal_os_Zygote_nativeSupportsTaggedPointers(JNIEnv* env, jclass) {
+#ifdef __aarch64__
+ int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ return res >= 0 && res & PR_TAGGED_ADDR_ENABLE;
+#else
+ return false;
+#endif
+}
+
static const JNINativeMethod gMethods[] = {
{"nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
- "String;Z[Ljava/lang/String;Z)I",
+ "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I",
(void*)com_android_internal_os_Zygote_nativeForkAndSpecialize},
{"nativeForkSystemServer", "(II[II[[IJJ)I",
(void*)com_android_internal_os_Zygote_nativeForkSystemServer},
@@ -2421,7 +2455,7 @@ static const JNINativeMethod gMethods[] = {
{"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap},
{"nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
- "String;Z[Ljava/lang/String;Z)V",
+ "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
(void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess},
{"nativeInitNativeState", "(Z)V",
(void*)com_android_internal_os_Zygote_nativeInitNativeState},
@@ -2440,6 +2474,8 @@ static const JNINativeMethod gMethods[] = {
(void*)com_android_internal_os_Zygote_nativeBoostUsapPriority},
{"nativeParseSigChld", "([BI[I)I",
(void*)com_android_internal_os_Zygote_nativeParseSigChld},
+ {"nativeSupportsTaggedPointers", "()Z",
+ (void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto
index 76f8f0febf59..61b9b25fe7cf 100644
--- a/core/proto/android/stats/dnsresolver/dns_resolver.proto
+++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto
@@ -62,6 +62,13 @@ enum NsRcode {
NS_R_NOTAUTH = 9; // Not authoritative for zone
NS_R_NOTZONE = 10; // Zone of record different from zone section
NS_R_MAX = 11;
+ // Define rcode=12~15(UNASSIGNED) in rcode enum type.
+ // Some DNS Servers might return undefined code to devices.
+ // Without the enum definition, that would be noise for our dashboard.
+ NS_R_UNASSIGNED12 = 12; // Unassigned
+ NS_R_UNASSIGNED13 = 13; // Unassigned
+ NS_R_UNASSIGNED14 = 14; // Unassigned
+ NS_R_UNASSIGNED15 = 15; // Unassigned
// The following are EDNS extended rcodes
NS_R_BADVERS = 16;
// The following are TSIG errors
@@ -170,12 +177,22 @@ enum NetworkType {
NT_BLUETOOTH = 3;
// Indicates this network uses an Ethernet transport.
NT_ETHERNET = 4;
- // Indicates this network uses a VPN transport.
- NT_VPN = 5;
+ // Indicates this network uses a VPN transport, now deprecated.
+ NT_VPN = 5 [deprecated=true];
// Indicates this network uses a Wi-Fi Aware transport.
NT_WIFI_AWARE = 6;
// Indicates this network uses a LoWPAN transport.
NT_LOWPAN = 7;
+ // Indicates this network uses a Cellular+VPN transport.
+ NT_CELLULAR_VPN = 8;
+ // Indicates this network uses a Wi-Fi+VPN transport.
+ NT_WIFI_VPN = 9;
+ // Indicates this network uses a Bluetooth+VPN transport.
+ NT_BLUETOOTH_VPN = 10;
+ // Indicates this network uses an Ethernet+VPN transport.
+ NT_ETHERNET_VPN = 11;
+ // Indicates this network uses a Wi-Fi+Cellular+VPN transport.
+ NT_WIFI_CELLULAR_VPN = 12;
}
enum CacheStatus{
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index cbb379bf8207..d432dda4a1be 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -696,8 +696,7 @@ public class InsetsControllerTest {
// Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will
// attempt to release mLeash directly.
- SurfaceControl copy = new SurfaceControl();
- copy.copyFrom(mLeash);
+ SurfaceControl copy = new SurfaceControl(mLeash);
return new InsetsSourceControl(type, copy, new Point());
}
diff --git a/packages/SystemUI/res/layout/bubble_dismiss_target.xml b/packages/SystemUI/res/layout/bubble_dismiss_target.xml
index ca085b69c35d..f5cd727a6d03 100644
--- a/packages/SystemUI/res/layout/bubble_dismiss_target.xml
+++ b/packages/SystemUI/res/layout/bubble_dismiss_target.xml
@@ -17,7 +17,7 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="@dimen/pip_dismiss_gradient_height"
+ android:layout_height="@dimen/floating_dismiss_gradient_height"
android:layout_gravity="bottom|center_horizontal">
<FrameLayout
diff --git a/packages/SystemUI/res/layout/pip_dismiss_view.xml b/packages/SystemUI/res/layout/pip_dismiss_view.xml
deleted file mode 100644
index 2cc4b220fe2b..000000000000
--- a/packages/SystemUI/res/layout/pip_dismiss_view.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/pip_dismiss_gradient_height"
- android:alpha="0">
-
- <TextView
- android:id="@+id/pip_dismiss_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|center_horizontal"
- android:text="@string/pip_phone_dismiss_hint"
- android:textColor="#FFFFFFFF"
- android:textSize="14sp"
- android:shadowColor="@android:color/black"
- android:shadowDx="-2"
- android:shadowDy="2"
- android:shadowRadius="0.01" />
-
-</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9b9fbed0d904..bce5fac76cfc 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -955,7 +955,7 @@
<dimen name="recents_quick_scrub_onboarding_margin_start">8dp</dimen>
<!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
- <dimen name="pip_dismiss_gradient_height">176dp</dimen>
+ <dimen name="floating_dismiss_gradient_height">176dp</dimen>
<!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. -->
<dimen name="pip_dismiss_text_bottom_margin">24dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
index d1544346a25a..af5196f92bcb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
@@ -24,6 +24,8 @@ import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
@@ -40,6 +42,7 @@ import androidx.palette.graphics.Palette;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.MediaControllerFactory;
import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
@@ -71,10 +74,11 @@ public class KeyguardMediaPlayer {
private KeyguardMediaObserver mObserver;
@Inject
- public KeyguardMediaPlayer(Context context, @Background Executor backgroundExecutor) {
+ public KeyguardMediaPlayer(Context context, MediaControllerFactory factory,
+ @Background Executor backgroundExecutor) {
mContext = context;
mBackgroundExecutor = backgroundExecutor;
- mViewModel = new KeyguardMediaViewModel(context);
+ mViewModel = new KeyguardMediaViewModel(context, factory);
}
/** Binds media controls to a view hierarchy. */
@@ -139,14 +143,16 @@ public class KeyguardMediaPlayer {
private static final class KeyguardMediaViewModel {
private final Context mContext;
+ private final MediaControllerFactory mMediaControllerFactory;
private final MutableLiveData<KeyguardMedia> mMedia = new MutableLiveData<>();
private final Object mActionsLock = new Object();
private List<PendingIntent> mActions;
private float mAlbumArtRadius;
private int mAlbumArtSize;
- KeyguardMediaViewModel(Context context) {
+ KeyguardMediaViewModel(Context context, MediaControllerFactory factory) {
mContext = context;
+ mMediaControllerFactory = factory;
loadDimens();
}
@@ -162,6 +168,17 @@ public class KeyguardMediaPlayer {
public void updateControls(NotificationEntry entry, Icon appIcon,
MediaMetadata mediaMetadata) {
+ // Check the playback state of the media controller. If it is null, then the session was
+ // probably destroyed. Don't update in this case.
+ final MediaSession.Token token = entry.getSbn().getNotification().extras
+ .getParcelable(Notification.EXTRA_MEDIA_SESSION);
+ final MediaController controller = token != null
+ ? mMediaControllerFactory.create(token) : null;
+ if (controller != null && controller.getPlaybackState() == null) {
+ clearControls();
+ return;
+ }
+
// Foreground and Background colors computed from album art
Notification notif = entry.getSbn().getNotification();
int fgColor = notif.color;
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 23fa645eff16..5442299881c0 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -120,7 +120,7 @@ public class ImageWallpaper extends WallpaperService {
private void init(DozeParameters dozeParameters) {
mIsHighEndGfx = ActivityManager.isHighEndGfx();
mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
- mNeedTransition = mIsHighEndGfx && !mDisplayNeedsBlanking;
+ mNeedTransition = false;
// We will preserve EGL context when we are in lock screen or aod
// to avoid janking in following transition, we need to release when back to home.
@@ -137,7 +137,7 @@ public class ImageWallpaper extends WallpaperService {
mRenderer = getRendererInstance();
getDisplayContext().getDisplay().getDisplayInfo(mDisplayInfo);
setFixedSizeAllowed(true);
- setOffsetNotificationsEnabled(true);
+ setOffsetNotificationsEnabled(mNeedTransition);
updateSurfaceSize();
}
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index 6923079dd5c4..13d6a9bef266 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -49,6 +49,8 @@ public class Interpolators {
public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
public static final Interpolator HEADS_UP_APPEAR = new HeadsUpAppearInterpolator();
public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
+ public static final Interpolator SHADE_ANIMATION =
+ new PathInterpolator(0.6f, 0.02f, 0.4f, 0.98f);
public static final Interpolator ICON_OVERSHOT_LESS
= new PathInterpolator(0.4f, 0f, 0.2f, 1.1f);
public static final Interpolator PANEL_CLOSE_ACCELERATED
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index eff693436451..044feaa117c8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -514,7 +514,7 @@ public class BubbleStackView extends FrameLayout {
mDismissTargetContainer = new FrameLayout(context);
mDismissTargetContainer.setLayoutParams(new FrameLayout.LayoutParams(
MATCH_PARENT,
- getResources().getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height),
+ getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
Gravity.BOTTOM));
mDismissTargetContainer.setClipChildren(false);
mDismissTargetContainer.addView(targetView);
@@ -523,7 +523,7 @@ public class BubbleStackView extends FrameLayout {
// Start translated down so the target springs up.
targetView.setTranslationY(
- getResources().getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height));
+ getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height));
// Save the MagneticTarget instance for the newly set up view - we'll add this to the
// MagnetizedObjects.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index c16dce12041d..3f88f252bfe7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -40,7 +40,7 @@ public class DozeDockHandler implements DozeMachine.Part {
private int mDockState = DockManager.STATE_NONE;
- public DozeDockHandler(AmbientDisplayConfiguration config, DozeMachine machine,
+ DozeDockHandler(AmbientDisplayConfiguration config, DozeMachine machine,
DockManager dockManager) {
mMachine = machine;
mConfig = config;
@@ -74,8 +74,13 @@ public class DozeDockHandler implements DozeMachine.Part {
@Override
public void onEvent(int dockState) {
if (DEBUG) Log.d(TAG, "dock event = " + dockState);
- final DozeMachine.State nextState;
+
mDockState = dockState;
+ if (isPulsing()) {
+ return;
+ }
+
+ DozeMachine.State nextState;
switch (mDockState) {
case DockManager.STATE_DOCKED:
nextState = State.DOZE_AOD_DOCKED;
@@ -90,10 +95,15 @@ public class DozeDockHandler implements DozeMachine.Part {
default:
return;
}
-
mMachine.requestState(nextState);
}
+ private boolean isPulsing() {
+ DozeMachine.State state = mMachine.getState();
+ return state == State.DOZE_REQUEST_PULSE || state == State.DOZE_PULSING
+ || state == State.DOZE_PULSING_BRIGHT;
+ }
+
void register() {
if (mRegistered) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index f7f9afdd2928..18bfd899a4e7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -339,8 +339,8 @@ public class DozeMachine {
return State.DOZE;
}
if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING
- || mState == State.DOZE_AOD || mState == State.DOZE)
- && requestedState == State.DOZE_PULSE_DONE) {
+ || mState == State.DOZE_AOD || mState == State.DOZE
+ || mState == State.DOZE_AOD_DOCKED) && requestedState == State.DOZE_PULSE_DONE) {
Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
return mState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java b/packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java
new file mode 100644
index 000000000000..71bc7c20c026
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media;
+
+import android.content.Context;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+
+import javax.inject.Inject;
+
+/**
+ * Testable wrapper around {@link MediaController} constructor.
+ */
+public class MediaControllerFactory {
+
+ private final Context mContext;
+
+ @Inject
+ public MediaControllerFactory(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Creates a new MediaController from a session's token.
+ *
+ * @param token The token for the session. This value must never be null.
+ */
+ public MediaController create(MediaSession.Token token) {
+ return new MediaController(mContext, token);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 1868536dca98..e24d29f1aedf 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -231,7 +231,12 @@ public class PipBoundsHandler {
/**
* @return {@link Rect} of the destination PiP window bounds.
*/
- Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize) {
+ Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds,
+ Size minimalSize) {
+ if (!componentName.equals(mLastPipComponentName)) {
+ onResetReentryBoundsUnchecked();
+ mLastPipComponentName = componentName;
+ }
final Rect destinationBounds;
if (bounds == null) {
final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
@@ -246,11 +251,7 @@ public class PipBoundsHandler {
transformBoundsToAspectRatio(destinationBounds, aspectRatio,
false /* useCurrentMinEdgeSize */);
}
- if (destinationBounds.equals(bounds)) {
- return bounds;
- }
mAspectRatio = aspectRatio;
- onResetReentryBoundsUnchecked();
mLastDestinationBounds.set(destinationBounds);
return destinationBounds;
}
@@ -483,6 +484,7 @@ public class PipBoundsHandler {
pw.println(prefix + TAG);
pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction);
+ pw.println(innerPrefix + "mReentrySize=" + mReentrySize);
pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio);
pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index d9872d7dcf17..b10dd93fa695 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -247,7 +247,7 @@ public class PipTaskOrganizer extends TaskOrganizer {
public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(info, "Requires RunningTaskInfo");
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(info.pictureInPictureParams),
+ info.topActivity, getAspectRatioOrDefault(info.pictureInPictureParams),
null /* bounds */, getMinimalSize(info.topActivityInfo));
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
mTaskInfo = info;
@@ -303,7 +303,7 @@ public class PipTaskOrganizer extends TaskOrganizer {
return;
}
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(newParams),
+ info.topActivity, getAspectRatioOrDefault(newParams),
null /* bounds */, getMinimalSize(info.topActivityInfo));
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
@@ -335,7 +335,7 @@ public class PipTaskOrganizer extends TaskOrganizer {
}
final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(mTaskInfo.pictureInPictureParams),
+ mTaskInfo.topActivity, getAspectRatioOrDefault(mTaskInfo.pictureInPictureParams),
null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
if (newDestinationBounds.equals(currentDestinationBounds)) return;
if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
deleted file mode 100644
index b7258117c48c..000000000000
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.pip.phone;
-
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.widget.FrameLayout;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-/**
- * Displays the dismiss UI and target for floating objects.
- */
-public class PipDismissViewController {
-
- // This delay controls how long to wait before we show the target when the user first moves
- // the PIP, to prevent the target from animating if the user just wants to fling the PIP
- public static final int SHOW_TARGET_DELAY = 100;
- private static final int SHOW_TARGET_DURATION = 350;
- private static final int HIDE_TARGET_DURATION = 225;
-
- private Context mContext;
- private WindowManager mWindowManager;
- private View mDismissView;
-
- // Used for dismissing a bubble -- bubble should be in the target to be considered a dismiss
- private View mTargetView;
- private int mTargetSlop;
- private Point mWindowSize;
- private int[] mLoc = new int[2];
- private boolean mIntersecting;
- private Vibrator mVibe;
-
- public PipDismissViewController(Context context) {
- mContext = context;
- mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- mVibe = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- }
-
- /**
- * Creates the dismiss target for showing via {@link #showDismissTarget()}.
- */
- public void createDismissTarget() {
- if (mDismissView == null) {
- // Determine sizes for the view
- final Rect stableInsets = new Rect();
- WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
- mWindowSize = new Point();
- mWindowManager.getDefaultDisplay().getRealSize(mWindowSize);
- final int gradientHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.pip_dismiss_gradient_height);
- final int bottomMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.pip_dismiss_text_bottom_margin);
- mTargetSlop = mContext.getResources().getDimensionPixelSize(
- R.dimen.bubble_dismiss_slop);
-
- // Create a new view for the dismiss target
- LayoutInflater inflater = LayoutInflater.from(mContext);
- mDismissView = inflater.inflate(R.layout.pip_dismiss_view, null);
- mDismissView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- mDismissView.forceHasOverlappingRendering(false);
-
- // Set the gradient background
- Drawable gradient = mContext.getResources().getDrawable(R.drawable.pip_dismiss_scrim);
- gradient.setAlpha((int) (255 * 0.85f));
- mDismissView.setBackground(gradient);
-
- // Adjust bottom margins of the text
- mTargetView = mDismissView.findViewById(R.id.pip_dismiss_text);
- FrameLayout.LayoutParams tlp = (FrameLayout.LayoutParams) mTargetView.getLayoutParams();
- tlp.bottomMargin = stableInsets.bottom + bottomMargin;
- mTargetView.setLayoutParams(tlp);
-
- // Add the target to the window
- LayoutParams lp = new LayoutParams(
- LayoutParams.MATCH_PARENT, gradientHeight,
- 0, mWindowSize.y - gradientHeight,
- LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | LayoutParams.FLAG_NOT_TOUCHABLE
- | LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT);
- lp.setTitle("pip-dismiss-overlay");
- lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.setFitInsetsTypes(0 /* types */);
- mWindowManager.addView(mDismissView, lp);
- }
- mDismissView.animate().cancel();
- }
-
-
- /**
- * Updates the dismiss target based on location of the view, only used for bubbles not for PIP.
- *
- * @return whether the view is within the dismiss target.
- */
- public boolean updateTarget(View view) {
- if (mDismissView == null) {
- return false;
- }
- if (mDismissView.getAlpha() > 0) {
- view.getLocationOnScreen(mLoc);
- Rect viewRect = new Rect(mLoc[0], mLoc[1], mLoc[0] + view.getWidth(),
- mLoc[1] + view.getHeight());
- mTargetView.getLocationOnScreen(mLoc);
- Rect targetRect = new Rect(mLoc[0], mLoc[1], mLoc[0] + mTargetView.getWidth(),
- mLoc[1] + mTargetView.getHeight());
- expandRect(targetRect, mTargetSlop);
- boolean intersecting = targetRect.intersect(viewRect);
- if (intersecting != mIntersecting) {
- // TODO: is this the right effect?
- mVibe.vibrate(VibrationEffect.get(intersecting
- ? VibrationEffect.EFFECT_CLICK
- : VibrationEffect.EFFECT_TICK));
- }
- mIntersecting = intersecting;
- return intersecting;
- }
- return false;
- }
-
- /**
- * Shows the dismiss target.
- */
- public void showDismissTarget() {
- mDismissView.animate()
- .alpha(1f)
- .setInterpolator(Interpolators.LINEAR)
- .setStartDelay(SHOW_TARGET_DELAY)
- .setDuration(SHOW_TARGET_DURATION)
- .start();
- }
-
- /**
- * Hides and destroys the dismiss target.
- */
- public void destroyDismissTarget() {
- if (mDismissView != null) {
- mDismissView.animate()
- .alpha(0f)
- .setInterpolator(Interpolators.LINEAR)
- .setStartDelay(0)
- .setDuration(HIDE_TARGET_DURATION)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mWindowManager.removeViewImmediate(mDismissView);
- mDismissView = null;
- }
- })
- .start();
- }
- }
-
- private void expandRect(Rect outRect, int expandAmount) {
- outRect.left = Math.max(0, outRect.left - expandAmount);
- outRect.top = Math.max(0, outRect.top - expandAmount);
- outRect.right = Math.min(mWindowSize.x, outRect.right + expandAmount);
- outRect.bottom = Math.min(mWindowSize.y, outRect.bottom + expandAmount);
- }
-}
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 8397c65dbdb0..a192afceddb9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -24,8 +24,6 @@ import android.annotation.Nullable;
import android.app.ActivityManager.StackInfo;
import android.app.IActivityTaskManager;
import android.content.Context;
-import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Debug;
import android.os.RemoteException;
@@ -40,6 +38,7 @@ 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;
+import com.android.systemui.util.magnetictarget.MagnetizedObject;
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -61,9 +60,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** Friction to use for PIP when it moves via physics fling animations. */
private static final float DEFAULT_FRICTION = 2f;
- // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
- private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f;
-
private final Context mContext;
private final IActivityTaskManager mActivityTaskManager;
private final PipTaskOrganizer mPipTaskOrganizer;
@@ -103,7 +99,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/**
* Update listener that resizes the PIP to {@link #mAnimatedBounds}.
*/
- private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
+ final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
(target, values) -> resizePipUnchecked(mAnimatedBounds);
/** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
@@ -122,6 +118,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private final Consumer<Rect> mUpdateBoundsCallback = mBounds::set;
+ /**
+ * Whether we're springing to the touch event location (vs. moving it to that position
+ * instantly). We spring-to-touch after PIP is dragged out of the magnetic target, since it was
+ * 'stuck' in the target and needs to catch up to the touch location.
+ */
+ private boolean mSpringingToTouch = false;
+
public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
@@ -211,9 +214,35 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
mFloatingContentCoordinator.onContentMoved(this);
}
- cancelAnimations();
- resizePipUnchecked(toBounds);
- mBounds.set(toBounds);
+ if (!mSpringingToTouch) {
+ // If we are moving PIP directly to the touch event locations, cancel any animations and
+ // move PIP to the given bounds.
+ cancelAnimations();
+ resizePipUnchecked(toBounds);
+ mBounds.set(toBounds);
+ } else {
+ // If PIP is 'catching up' after being stuck in the dismiss target, update the animation
+ // to spring towards the new touch location.
+ mAnimatedBoundsPhysicsAnimator
+ .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
+ .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig)
+ .withEndActions(() -> mSpringingToTouch = false);
+
+ startBoundsAnimator(toBounds.left /* toX */, toBounds.top /* toY */);
+ }
+ }
+
+ /** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */
+ void setSpringingToTouch(boolean springingToTouch) {
+ if (springingToTouch) {
+ mAnimatedBounds.set(mBounds);
+ }
+
+ mSpringingToTouch = springingToTouch;
+ }
+
+ void prepareForAnimation() {
+ mAnimatedBounds.set(mBounds);
}
/**
@@ -278,24 +307,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
}
/**
- * @return whether the PiP at the current bounds should be dismissed.
- */
- boolean shouldDismissPip() {
- Point displaySize = new Point();
- mContext.getDisplay().getRealSize(displaySize);
- final int y = displaySize.y - mStableInsets.bottom;
- if (mBounds.bottom > y) {
- float offscreenFraction = (float) (mBounds.bottom - y) / mBounds.height();
- return offscreenFraction >= DISMISS_OFFSCREEN_FRACTION;
- }
- return false;
- }
-
- /**
* Flings the PiP to the closest snap target.
*/
void flingToSnapTarget(
- float velocityX, float velocityY, Runnable updateAction, @Nullable Runnable endAction) {
+ float velocityX, float velocityY,
+ @Nullable Runnable updateAction, @Nullable Runnable endAction) {
mAnimatedBounds.set(mBounds);
mAnimatedBoundsPhysicsAnimator
.flingThenSpring(
@@ -303,9 +319,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
true /* flingMustReachMinOrMax */)
.flingThenSpring(
FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig)
- .addUpdateListener((target, values) -> updateAction.run())
.withEndActions(endAction);
+ if (updateAction != null) {
+ mAnimatedBoundsPhysicsAnimator.addUpdateListener(
+ (target, values) -> updateAction.run());
+ }
+
final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right;
final float estimatedFlingYEndValue =
PhysicsAnimator.estimateFlingEndValue(mBounds.top, velocityY, mFlingConfigY);
@@ -338,16 +358,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* Animates the dismissal of the PiP off the edge of the screen.
*/
void animateDismiss(float velocityX, float velocityY, @Nullable Runnable updateAction) {
- final float velocity = PointF.length(velocityX, velocityY);
- final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
- final Point dismissEndPoint = getDismissEndPoint(mBounds, velocityX, velocityY, isFling);
-
mAnimatedBounds.set(mBounds);
- // Animate to the dismiss end point, and then dismiss PIP.
+ // Animate off the bottom of the screen, then dismiss PIP.
mAnimatedBoundsPhysicsAnimator
- .spring(FloatProperties.RECT_X, dismissEndPoint.x, velocityX, mSpringConfig)
- .spring(FloatProperties.RECT_Y, dismissEndPoint.y, velocityY, mSpringConfig)
+ .spring(FloatProperties.RECT_Y,
+ mBounds.bottom + mBounds.height(),
+ velocityY,
+ mSpringConfig)
.withEndActions(this::dismissPip);
// If we were provided with an update action, run it whenever there's an update.
@@ -356,7 +374,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
(target, values) -> updateAction.run());
}
- startBoundsAnimator(dismissEndPoint.x /* toX */, dismissEndPoint.y /* toY */);
+ startBoundsAnimator(mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */);
}
/**
@@ -408,6 +426,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private void cancelAnimations() {
mAnimatedBoundsPhysicsAnimator.cancel();
mAnimatingToBounds.setEmpty();
+ mSpringingToTouch = false;
}
/** Set new fling configs whose min/max values respect the given movement bounds. */
@@ -426,7 +445,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* the 'real' bounds to equal the final animated bounds.
*/
private void startBoundsAnimator(float toX, float toY) {
- cancelAnimations();
+ if (!mSpringingToTouch) {
+ cancelAnimations();
+ }
// Set animatingToBounds directly to avoid allocating a new Rect, but then call
// setAnimatingToBounds to run the normal logic for changing animatingToBounds.
@@ -484,47 +505,29 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
}
/**
- * @return the coordinates the PIP should animate to based on the direction of velocity when
- * dismissing.
+ * Returns a MagnetizedObject wrapper for PIP's animated bounds. This is provided to the
+ * magnetic dismiss target so it can calculate PIP's size and position.
*/
- private Point getDismissEndPoint(Rect pipBounds, float velX, float velY, boolean isFling) {
- Point displaySize = new Point();
- mContext.getDisplay().getRealSize(displaySize);
- final float bottomBound = displaySize.y + pipBounds.height() * .1f;
- if (isFling && velX != 0 && velY != 0) {
- // Line is defined by: y = mx + b, m = slope, b = y-intercept
- // Find the slope
- final float slope = velY / velX;
- // Sub in slope and PiP position to solve for y-intercept: b = y - mx
- final float yIntercept = pipBounds.top - slope * pipBounds.left;
- // Now find the point on this line when y = bottom bound: x = (y - b) / m
- final float x = (bottomBound - yIntercept) / slope;
- return new Point((int) x, (int) bottomBound);
- } else {
- // If it wasn't a fling the velocity on 'up' is not reliable for direction of movement,
- // just animate downwards.
- return new Point(pipBounds.left, (int) bottomBound);
- }
- }
+ MagnetizedObject<Rect> getMagnetizedPip() {
+ return new MagnetizedObject<Rect>(
+ mContext, mAnimatedBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) {
+ @Override
+ public float getWidth(@NonNull Rect animatedPipBounds) {
+ return animatedPipBounds.width();
+ }
- /**
- * @return whether the gesture it towards the dismiss area based on the velocity when
- * dismissing.
- */
- public boolean isGestureToDismissArea(Rect pipBounds, float velX, float velY,
- boolean isFling) {
- Point endpoint = getDismissEndPoint(pipBounds, velX, velY, isFling);
- // Center the point
- endpoint.x += pipBounds.width() / 2;
- endpoint.y += pipBounds.height() / 2;
-
- // The dismiss area is the middle third of the screen, half the PIP's height from the bottom
- Point size = new Point();
- mContext.getDisplay().getRealSize(size);
- final int left = size.x / 3;
- Rect dismissArea = new Rect(left, size.y - (pipBounds.height() / 2), left * 2,
- size.y + pipBounds.height());
- return dismissArea.contains(endpoint.x, endpoint.y);
+ @Override
+ public float getHeight(@NonNull Rect animatedPipBounds) {
+ return animatedPipBounds.height();
+ }
+
+ @Override
+ public void getLocationOnScreen(
+ @NonNull Rect animatedPipBounds, @NonNull int[] loc) {
+ loc[0] = animatedPipBounds.left;
+ loc[1] = animatedPipBounds.top;
+ }
+ };
}
public void dump(PrintWriter pw, String prefix) {
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 7cc2759ad59a..bbb493966533 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -20,11 +20,13 @@ import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STAT
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
+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;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -33,14 +35,23 @@ import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
import android.util.Size;
+import android.view.Gravity;
import android.view.IPinnedStackController;
import android.view.InputEvent;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -51,7 +62,10 @@ import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.util.animation.PhysicsAnimator;
+import com.android.systemui.util.magnetictarget.MagnetizedObject;
import java.io.PrintWriter;
@@ -62,9 +76,6 @@ import java.io.PrintWriter;
public class PipTouchHandler {
private static final String TAG = "PipTouchHandler";
- // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
- private static final boolean ENABLE_FLING_DISMISS = false;
-
private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
private static final int BOTTOM_OFFSET_BUFFER_DP = 1;
@@ -73,17 +84,45 @@ public class PipTouchHandler {
// Allow PIP to resize to a slightly bigger state upon touch
private final boolean mEnableResize;
private final Context mContext;
+ private final WindowManager mWindowManager;
private final IActivityManager mActivityManager;
private final PipBoundsHandler mPipBoundsHandler;
private PipResizeGestureHandler mPipResizeGestureHandler;
private IPinnedStackController mPinnedStackController;
private final PipMenuActivityController mMenuController;
- private final PipDismissViewController mDismissViewController;
private final PipSnapAlgorithm mSnapAlgorithm;
private final AccessibilityManager mAccessibilityManager;
private boolean mShowPipMenuOnAnimationEnd = false;
+ /**
+ * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
+ * PIP.
+ */
+ private MagnetizedObject<Rect> mMagnetizedPip;
+
+ /**
+ * Container for the dismiss circle, so that it can be animated within the container via
+ * translation rather than within the WindowManager via slow layout animations.
+ */
+ private ViewGroup mTargetViewContainer;
+
+ /** Circle view used to render the dismiss target. */
+ private DismissCircleView mTargetView;
+
+ /**
+ * MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius.
+ */
+ private MagnetizedObject.MagneticTarget mMagneticTarget;
+
+ /** PhysicsAnimator instance for animating the dismiss target in/out. */
+ private PhysicsAnimator<View> mMagneticTargetAnimator;
+
+ /** Default configuration to use for springing the dismiss target in/out. */
+ private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
+ new PhysicsAnimator.SpringConfig(
+ SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_NO_BOUNCY);
+
// The current movement bounds
private Rect mMovementBounds = new Rect();
// The current resized bounds, changed by user resize.
@@ -104,21 +143,20 @@ public class PipTouchHandler {
private int mDeferResizeToNormalBoundsUntilRotation = -1;
private int mDisplayRotation;
+ /**
+ * Runnable that can be posted delayed to show the target. This needs to be saved as a member
+ * variable so we can pass it to removeCallbacks.
+ */
+ private Runnable mShowTargetAction = this::showDismissTargetMaybe;
+
private Handler mHandler = new Handler();
- private Runnable mShowDismissAffordance = new Runnable() {
- @Override
- public void run() {
- if (mEnableDismissDragToEdge) {
- mDismissViewController.showDismissTarget();
- }
- }
- };
// Behaviour states
private int mMenuState = MENU_STATE_NONE;
private boolean mIsImeShowing;
private int mImeHeight;
private int mImeOffset;
+ private int mDismissAreaHeight;
private boolean mIsShelfShowing;
private int mShelfHeight;
private int mMovementBoundsExtraOffsets;
@@ -168,6 +206,7 @@ public class PipTouchHandler {
}
}
+ @SuppressLint("InflateParams")
public PipTouchHandler(Context context, IActivityManager activityManager,
IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
InputConsumerController inputConsumerController,
@@ -180,9 +219,9 @@ public class PipTouchHandler {
mContext = context;
mActivityManager = activityManager;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mMenuController = menuController;
mMenuController.addListener(new PipMenuListener());
- mDismissViewController = new PipDismissViewController(context);
mSnapAlgorithm = pipSnapAlgorithm;
mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(),
2.5f);
@@ -200,6 +239,7 @@ public class PipTouchHandler {
mExpandedShortestEdgeSize = res.getDimensionPixelSize(
R.dimen.pip_expanded_shortest_edge_size);
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
+ mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
@@ -212,6 +252,56 @@ public class PipTouchHandler {
mFloatingContentCoordinator = floatingContentCoordinator;
mConnection = new PipAccessibilityInteractionConnection(mMotionHelper,
this::onAccessibilityShowMenu, mHandler);
+
+ final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
+ mTargetView = new DismissCircleView(context);
+ final FrameLayout.LayoutParams newParams =
+ new FrameLayout.LayoutParams(targetSize, targetSize);
+ newParams.gravity = Gravity.CENTER;
+ mTargetView.setLayoutParams(newParams);
+
+ mTargetViewContainer = new FrameLayout(context);
+ mTargetViewContainer.setClipChildren(false);
+ mTargetViewContainer.addView(mTargetView);
+
+ mMagnetizedPip = mMotionHelper.getMagnetizedPip();
+ mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
+ mMagnetizedPip.setPhysicsAnimatorUpdateListener(mMotionHelper.mResizePipUpdateListener);
+ mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
+ @Override
+ public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+ mMotionHelper.prepareForAnimation();
+
+ // Show the dismiss target, in case the initial touch event occurred within the
+ // magnetic field radius.
+ showDismissTargetMaybe();
+ }
+
+ @Override
+ public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
+ float velX, float velY, boolean wasFlungOut) {
+ if (wasFlungOut) {
+ mMotionHelper.flingToSnapTarget(velX, velY, null, null);
+ hideDismissTarget();
+ } else {
+ mMotionHelper.setSpringingToTouch(true);
+ }
+ }
+
+ @Override
+ public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+ mHandler.post(() -> {
+ mMotionHelper.animateDismiss(0, 0, null);
+ hideDismissTarget();
+ });
+
+
+ MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
+ PipUtils.getTopPipActivity(mContext, mActivityManager));
+ }
+ });
+
+ mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
}
public void setTouchGesture(PipTouchGesture gesture) {
@@ -231,7 +321,8 @@ public class PipTouchHandler {
}
public void onActivityPinned() {
- cleanUpDismissTarget();
+ createDismissTargetMaybe();
+
mShowPipMenuOnAnimationEnd = true;
mPipResizeGestureHandler.onActivityPinned();
mFloatingContentCoordinator.onContentAdded(mMotionHelper);
@@ -264,6 +355,10 @@ public class PipTouchHandler {
public void onConfigurationChanged() {
mMotionHelper.onConfigurationChanged();
mMotionHelper.synchronizePinnedStackBounds();
+
+ // Recreate the dismiss target for the new orientation.
+ cleanUpDismissTarget();
+ createDismissTargetMaybe();
}
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
@@ -351,6 +446,74 @@ public class PipTouchHandler {
}
}
+ /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
+ private void createDismissTargetMaybe() {
+ if (!mTargetViewContainer.isAttachedToWindow()) {
+ mHandler.removeCallbacks(mShowTargetAction);
+ mMagneticTargetAnimator.cancel();
+
+ final Point windowSize = new Point();
+ mWindowManager.getDefaultDisplay().getRealSize(windowSize);
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ mDismissAreaHeight,
+ 0, windowSize.y - mDismissAreaHeight,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ lp.setTitle("pip-dismiss-overlay");
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setFitInsetsTypes(0 /* types */);
+
+ mTargetViewContainer.setVisibility(View.INVISIBLE);
+ mWindowManager.addView(mTargetViewContainer, lp);
+ }
+ }
+
+ /** Makes the dismiss target visible and animates it in, if it isn't already visible. */
+ private void showDismissTargetMaybe() {
+ createDismissTargetMaybe();
+
+ if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
+
+ mTargetView.setTranslationY(mTargetViewContainer.getHeight());
+ mTargetViewContainer.setVisibility(View.VISIBLE);
+
+ // Set the magnetic field radius to half of PIP's width.
+ mMagneticTarget.setMagneticFieldRadiusPx(mMotionHelper.getBounds().width());
+
+ // Cancel in case we were in the middle of animating it out.
+ mMagneticTargetAnimator.cancel();
+ mMagneticTargetAnimator
+ .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig)
+ .start();
+ }
+ }
+
+ /** Animates the magnetic dismiss target out and then sets it to GONE. */
+ private void hideDismissTarget() {
+ mHandler.removeCallbacks(mShowTargetAction);
+ mMagneticTargetAnimator
+ .spring(DynamicAnimation.TRANSLATION_Y,
+ mTargetViewContainer.getHeight(),
+ mTargetSpringConfig)
+ .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
+ .start();
+ }
+
+ /**
+ * Removes the dismiss target and cancels any pending callbacks to show it.
+ */
+ private void cleanUpDismissTarget() {
+ mHandler.removeCallbacks(mShowTargetAction);
+
+ if (mTargetViewContainer.isAttachedToWindow()) {
+ mWindowManager.removeView(mTargetViewContainer);
+ }
+ }
+
private void onRegistrationChanged(boolean isRegistered) {
mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
? mConnection : null);
@@ -375,8 +538,24 @@ public class PipTouchHandler {
if (mPinnedStackController == null) {
return true;
}
+
MotionEvent ev = (MotionEvent) inputEvent;
+ if (mMagnetizedPip.maybeConsumeMotionEvent(ev)) {
+ // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
+ // to the touch state. Touch state needs a DOWN event in order to later process MOVE
+ // events it'll receive if the object is dragged out of the magnetic field.
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mTouchState.onTouchEvent(ev);
+ }
+
+ // Continue tracking velocity when the object is in the magnetic field, since we want to
+ // respect touch input velocity if the object is dragged out and then flung.
+ mTouchState.addMovementToVelocityTracker(ev);
+
+ return true;
+ }
+
// Update the touch state
mTouchState.onTouchEvent(ev);
@@ -600,17 +779,13 @@ public class PipTouchHandler {
mDelta.set(0f, 0f);
mStartPosition.set(bounds.left, bounds.top);
mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
+ mMotionHelper.setSpringingToTouch(false);
// If the menu is still visible then just poke the menu
// so that it will timeout after the user stops touching it
if (mMenuState != MENU_STATE_NONE) {
mMenuController.pokeMenu();
}
-
- if (mEnableDismissDragToEdge) {
- mDismissViewController.createDismissTarget();
- mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
- }
}
@Override
@@ -623,8 +798,10 @@ public class PipTouchHandler {
mSavedSnapFraction = -1f;
if (mEnableDismissDragToEdge) {
- mHandler.removeCallbacks(mShowDismissAffordance);
- mDismissViewController.showDismissTarget();
+ if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
+ mHandler.removeCallbacks(mShowTargetAction);
+ showDismissTargetMaybe();
+ }
}
}
@@ -644,10 +821,6 @@ public class PipTouchHandler {
mTmpBounds.offsetTo((int) left, (int) top);
mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
- if (mEnableDismissDragToEdge) {
- updateDismissFraction();
- }
-
final PointF curPos = touchState.getLastTouchPosition();
if (mMovementWithinDismiss) {
// Track if movement remains near the bottom edge to identify swipe to dismiss
@@ -661,9 +834,7 @@ public class PipTouchHandler {
@Override
public boolean onUp(PipTouchState touchState) {
if (mEnableDismissDragToEdge) {
- // Clean up the dismiss target regardless of the touch state in case the touch
- // enabled state changes while the user is interacting
- cleanUpDismissTarget();
+ hideDismissTarget();
}
if (!touchState.isUserInteracting()) {
@@ -671,26 +842,8 @@ public class PipTouchHandler {
}
final PointF vel = touchState.getVelocity();
- final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
final float velocity = PointF.length(vel.x, vel.y);
final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
- final boolean isUpWithinDimiss = ENABLE_FLING_DISMISS
- && touchState.getLastTouchPosition().y >= mMovementBounds.bottom
- && mMotionHelper.isGestureToDismissArea(mMotionHelper.getBounds(), vel.x,
- vel.y, isFling);
- final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal
- && (mMovementWithinDismiss || isUpWithinDimiss);
- if (mEnableDismissDragToEdge) {
- // Check if the user dragged or flung the PiP offscreen to dismiss it
- if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
- MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
- PipUtils.getTopPipActivity(mContext, mActivityManager));
- mMotionHelper.animateDismiss(
- vel.x, vel.y,
- PipTouchHandler.this::updateDismissFraction /* updateAction */);
- return true;
- }
- }
if (touchState.isDragging()) {
Runnable endAction = null;
@@ -749,14 +902,6 @@ public class PipTouchHandler {
}
/**
- * Removes the dismiss target and cancels any pending callbacks to show it.
- */
- private void cleanUpDismissTarget() {
- mHandler.removeCallbacks(mShowDismissAffordance);
- mDismissViewController.destroyDismissTarget();
- }
-
- /**
* @return whether the menu will resize as a part of showing the full menu.
*/
private boolean willResizeMenu() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index e3f65ef812fb..dc286c1c2de5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -92,7 +92,7 @@ public class PipTouchState {
// Initialize the velocity tracker
initOrResetVelocityTracker();
- addMovement(ev);
+ addMovementToVelocityTracker(ev);
mActivePointerId = ev.getPointerId(0);
if (DEBUG) {
@@ -120,7 +120,7 @@ public class PipTouchState {
}
// Update the velocity tracker
- addMovement(ev);
+ addMovementToVelocityTracker(ev);
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
Log.e(TAG, "Invalid active pointer id on MOVE: " + mActivePointerId);
@@ -151,7 +151,7 @@ public class PipTouchState {
}
// Update the velocity tracker
- addMovement(ev);
+ addMovementToVelocityTracker(ev);
int pointerIndex = ev.getActionIndex();
int pointerId = ev.getPointerId(pointerIndex);
@@ -174,7 +174,7 @@ public class PipTouchState {
}
// Update the velocity tracker
- addMovement(ev);
+ addMovementToVelocityTracker(ev);
mVelocityTracker.computeCurrentVelocity(1000,
mViewConfig.getScaledMaximumFlingVelocity());
mVelocity.set(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
@@ -318,6 +318,20 @@ public class PipTouchState {
return -1;
}
+ void addMovementToVelocityTracker(MotionEvent event) {
+ if (mVelocityTracker == null) {
+ return;
+ }
+
+ // Add movement to velocity tracker using raw screen X and Y coordinates instead
+ // of window coordinates because the window frame may be moving at the same time.
+ float deltaX = event.getRawX() - event.getX();
+ float deltaY = event.getRawY() - event.getY();
+ event.offsetLocation(deltaX, deltaY);
+ mVelocityTracker.addMovement(event);
+ event.offsetLocation(-deltaX, -deltaY);
+ }
+
private void initOrResetVelocityTracker() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
@@ -333,16 +347,6 @@ public class PipTouchState {
}
}
- private void addMovement(MotionEvent event) {
- // Add movement to velocity tracker using raw screen X and Y coordinates instead
- // of window coordinates because the window frame may be moving at the same time.
- float deltaX = event.getRawX() - event.getX();
- float deltaY = event.getRawY() - event.getY();
- event.offsetLocation(deltaX, deltaY);
- mVelocityTracker.addMovement(event);
- event.offsetLocation(-deltaX, -deltaY);
- }
-
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
index 9c175bc2b756..8efeef1ffa0a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
@@ -30,6 +30,14 @@ import com.android.systemui.R;
*/
public class PipControlsView extends LinearLayout {
+ public PipControlsView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index d2da2628276a..729f934937da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.WallpaperManager
import android.util.Log
+import android.util.MathUtils
import android.view.Choreographer
import android.view.View
import androidx.annotation.VisibleForTesting
@@ -37,6 +38,7 @@ import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.NotificationShadeWindowController
import com.android.systemui.statusbar.phone.PanelExpansionListener
+import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.KeyguardStateController
import java.io.FileDescriptor
import java.io.PrintWriter
@@ -106,6 +108,16 @@ class NotificationShadeDepthController @Inject constructor(
}
/**
+ * Force stop blur effect when necessary.
+ */
+ private var scrimsVisible: Boolean = false
+ set(value) {
+ if (field == value) return
+ field = value
+ scheduleUpdate()
+ }
+
+ /**
* Blur radius of the wake-up animation on this frame.
*/
private var wakeAndUnlockBlurRadius = 0
@@ -141,7 +153,13 @@ class NotificationShadeDepthController @Inject constructor(
if (showingHomeControls) {
globalActionsRadius = 0
}
- val blur = max(shadeRadius.toInt(), globalActionsRadius)
+ var blur = max(shadeRadius.toInt(), globalActionsRadius)
+
+ // Make blur be 0 if it is necessary to stop blur effect.
+ if (scrimsVisible) {
+ blur = 0
+ }
+
blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
try {
wallpaperManager.setWallpaperZoomOut(root.windowToken,
@@ -201,6 +219,10 @@ class NotificationShadeDepthController @Inject constructor(
brightnessMirrorSpring.finishIfRunning()
}
}
+
+ override fun onDozeAmountChanged(linear: Float, eased: Float) {
+ wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased)
+ }
}
init {
@@ -209,6 +231,10 @@ class NotificationShadeDepthController @Inject constructor(
keyguardStateController.addCallback(keyguardStateCallback)
}
statusBarStateController.addCallback(statusBarStateCallback)
+ notificationShadeWindowController.setScrimsVisibilityListener {
+ // Stop blur effect when scrims is opaque to avoid unnecessary GPU composition.
+ visibility -> scrimsVisible = visibility == ScrimController.OPAQUE
+ }
}
/**
@@ -224,8 +250,12 @@ class NotificationShadeDepthController @Inject constructor(
private fun updateShadeBlur() {
var newBlur = 0
- if (statusBarStateController.state == StatusBarState.SHADE) {
- newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
+ val state = statusBarStateController.state
+ if (state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) {
+ val animatedBlur =
+ Interpolators.SHADE_ANIMATION.getInterpolation(
+ MathUtils.constrain(shadeExpansion / 0.15f, 0f, 1f))
+ newBlur = blurUtils.blurRadiusOfRatio(0.35f * animatedBlur + 0.65f * shadeExpansion)
}
shadeSpring.animateTo(newBlur)
}
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 e1e679f06eef..462b42a44c17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -61,6 +61,7 @@ import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -92,6 +93,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
private final State mCurrentState = new State();
private OtherwisedCollapsedListener mListener;
private ForcePluginOpenListener mForcePluginOpenListener;
+ private Consumer<Integer> mScrimsVisibilityListener;
private final ArrayList<WeakReference<StatusBarWindowCallback>>
mCallbacks = Lists.newArrayList();
@@ -150,6 +152,16 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback));
}
+ /**
+ * Register a listener to monitor scrims visibility
+ * @param listener A listener to monitor scrims visibility
+ */
+ public void setScrimsVisibilityListener(Consumer<Integer> listener) {
+ if (listener != null && mScrimsVisibilityListener != listener) {
+ mScrimsVisibilityListener = listener;
+ }
+ }
+
private boolean shouldEnableKeyguardScreenRotation() {
Resources res = mContext.getResources();
return SystemProperties.getBoolean("lockscreen.rot_override", false)
@@ -477,6 +489,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
public void setScrimsVisibility(int scrimsVisibility) {
mCurrentState.mScrimsVisibility = scrimsVisibility;
apply(mCurrentState);
+ mScrimsVisibilityListener.accept(scrimsVisibility);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
index 812a1e4bc121..f27bdbfbeda0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
@@ -160,6 +160,18 @@ abstract class MagnetizedObject<T : Any>(
lateinit var magnetListener: MagnetizedObject.MagnetListener
/**
+ * Optional update listener to provide to the PhysicsAnimator that is used to spring the object
+ * into the target.
+ */
+ var physicsAnimatorUpdateListener: PhysicsAnimator.UpdateListener<T>? = null
+
+ /**
+ * Optional end listener to provide to the PhysicsAnimator that is used to spring the object
+ * into the target.
+ */
+ var physicsAnimatorEndListener: PhysicsAnimator.EndListener<T>? = null
+
+ /**
* Sets whether forcefully flinging the object vertically towards a target causes it to be
* attracted to the target and then released immediately, despite never being dragged within the
* magnetic field.
@@ -479,6 +491,14 @@ abstract class MagnetizedObject<T : Any>(
.spring(yProperty, yProperty.getValue(underlyingObject) + yDiff, velY,
springConfig)
+ if (physicsAnimatorUpdateListener != null) {
+ animator.addUpdateListener(physicsAnimatorUpdateListener!!)
+ }
+
+ if (physicsAnimatorEndListener != null) {
+ animator.addEndListener(physicsAnimatorEndListener!!)
+ }
+
if (after != null) {
animator.withEndActions(after)
}
@@ -560,13 +580,15 @@ abstract class MagnetizedObject<T : Any>(
private val tempLoc = IntArray(2)
fun updateLocationOnScreen() {
- targetView.getLocationOnScreen(tempLoc)
-
- // Add half of the target size to get the center, and subtract translation since the
- // target could be animating in while we're doing this calculation.
- centerOnScreen.set(
- tempLoc[0] + targetView.width / 2f - targetView.translationX,
- tempLoc[1] + targetView.height / 2f - targetView.translationY)
+ targetView.post {
+ targetView.getLocationOnScreen(tempLoc)
+
+ // Add half of the target size to get the center, and subtract translation since the
+ // target could be animating in while we're doing this calculation.
+ centerOnScreen.set(
+ tempLoc[0] + targetView.width / 2f - targetView.translationX,
+ tempLoc[1] + targetView.height / 2f - targetView.translationY)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
index 072bc446fd21..4bcf917fa95d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
@@ -16,8 +16,12 @@
package com.android.keyguard
+import android.app.Notification
import android.graphics.drawable.Icon
import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -28,7 +32,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.media.MediaControllerFactory
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -38,6 +44,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.any
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -48,9 +55,12 @@ import org.mockito.Mockito.`when` as whenever
public class KeyguardMediaPlayerTest : SysuiTestCase() {
private lateinit var keyguardMediaPlayer: KeyguardMediaPlayer
+ @Mock private lateinit var mockMediaFactory: MediaControllerFactory
+ @Mock private lateinit var mockMediaController: MediaController
+ private lateinit var playbackState: PlaybackState
private lateinit var fakeExecutor: FakeExecutor
private lateinit var mediaMetadata: MediaMetadata.Builder
- private lateinit var entry: NotificationEntryBuilder
+ private lateinit var entry: NotificationEntry
@Mock private lateinit var mockView: View
private lateinit var songView: TextView
private lateinit var artistView: TextView
@@ -70,8 +80,16 @@ public class KeyguardMediaPlayerTest : SysuiTestCase() {
@Before
public fun setup() {
+ playbackState = PlaybackState.Builder().run {
+ build()
+ }
+ mockMediaController = mock(MediaController::class.java)
+ whenever(mockMediaController.getPlaybackState()).thenReturn(playbackState)
+ mockMediaFactory = mock(MediaControllerFactory::class.java)
+ whenever(mockMediaFactory.create(any())).thenReturn(mockMediaController)
+
fakeExecutor = FakeExecutor(FakeSystemClock())
- keyguardMediaPlayer = KeyguardMediaPlayer(context, fakeExecutor)
+ keyguardMediaPlayer = KeyguardMediaPlayer(context, mockMediaFactory, fakeExecutor)
mockIcon = mock(Icon::class.java)
mockView = mock(View::class.java)
@@ -81,7 +99,9 @@ public class KeyguardMediaPlayerTest : SysuiTestCase() {
whenever<TextView>(mockView.findViewById(R.id.header_artist)).thenReturn(artistView)
mediaMetadata = MediaMetadata.Builder()
- entry = NotificationEntryBuilder()
+ entry = NotificationEntryBuilder().build()
+ entry.getSbn().getNotification().extras.putParcelable(Notification.EXTRA_MEDIA_SESSION,
+ MediaSession.Token(1, null))
ArchTaskExecutor.getInstance().setDelegate(taskExecutor)
@@ -109,7 +129,7 @@ public class KeyguardMediaPlayerTest : SysuiTestCase() {
@Test
public fun testUpdateControls() {
- keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+ keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
FakeExecutor.exhaustExecutors(fakeExecutor)
verify(mockView).setVisibility(View.VISIBLE)
}
@@ -122,11 +142,22 @@ public class KeyguardMediaPlayerTest : SysuiTestCase() {
}
@Test
+ public fun testUpdateControlsNullPlaybackState() {
+ // GIVEN that the playback state is null (ie. the media session was destroyed)
+ whenever(mockMediaController.getPlaybackState()).thenReturn(null)
+ // WHEN updated
+ keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
+ FakeExecutor.exhaustExecutors(fakeExecutor)
+ // THEN the controls are cleared (ie. visibility is set to GONE)
+ verify(mockView).setVisibility(View.GONE)
+ }
+
+ @Test
public fun testSongName() {
val song: String = "Song"
mediaMetadata.putText(MediaMetadata.METADATA_KEY_TITLE, song)
- keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+ keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
assertThat(songView.getText()).isEqualTo(song)
@@ -137,7 +168,7 @@ public class KeyguardMediaPlayerTest : SysuiTestCase() {
val artist: String = "Artist"
mediaMetadata.putText(MediaMetadata.METADATA_KEY_ARTIST, artist)
- keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+ keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
assertThat(artistView.getText()).isEqualTo(artist)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 475023e2506d..5227aaf01249 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -160,7 +160,7 @@ public class ImageWallpaperTest extends SysuiTestCase {
LOW_BMP_HEIGHT /* bmpHeight */,
LOW_BMP_WIDTH /* surfaceWidth */,
LOW_BMP_HEIGHT /* surfaceHeight */,
- true /* assertion */);
+ false /* assertion */);
}
@Test
@@ -172,7 +172,7 @@ public class ImageWallpaperTest extends SysuiTestCase {
INVALID_BMP_HEIGHT /* bmpHeight */,
ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */,
- true /* assertion */);
+ false /* assertion */);
}
private void verifySurfaceSizeAndAssertTransition(int bmpWidth, int bmpHeight,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index c9bb4016c7bf..9985d21e8515 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -20,8 +20,10 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.hardware.display.AmbientDisplayConfiguration;
import android.testing.AndroidTestingRunner;
@@ -56,6 +58,7 @@ public class DozeDockHandlerTest extends SysuiTestCase {
mDockManagerFake = spy(new DockManagerFake());
mDockHandler = new DozeDockHandler(mConfig, mMachine, mDockManagerFake);
+ when(mMachine.getState()).thenReturn(State.DOZE_AOD);
doReturn(true).when(mConfig).alwaysOnEnabled(anyInt());
mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
}
@@ -101,4 +104,31 @@ public class DozeDockHandlerTest extends SysuiTestCase {
verify(mMachine).requestState(eq(State.DOZE));
}
+
+ @Test
+ public void onEvent_dockedWhilePulsing_wontRequestStateChange() {
+ when(mMachine.getState()).thenReturn(State.DOZE_PULSING);
+
+ mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
+
+ verify(mMachine, never()).requestState(any(State.class));
+ }
+
+ @Test
+ public void onEvent_noneWhilePulsing_wontRequestStateChange() {
+ when(mMachine.getState()).thenReturn(State.DOZE_PULSING);
+
+ mDockManagerFake.setDockEvent(DockManager.STATE_NONE);
+
+ verify(mMachine, never()).requestState(any(State.class));
+ }
+
+ @Test
+ public void onEvent_hideWhilePulsing_wontRequestStateChange() {
+ when(mMachine.getState()).thenReturn(State.DOZE_PULSING);
+
+ mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
+
+ verify(mMachine, never()).requestState(any(State.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index c483314918fc..1f07f46bf764 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -254,6 +254,17 @@ public class DozeMachineTest extends SysuiTestCase {
}
@Test
+ public void testPulseDone_whileDockedAoD_staysDockedAod() {
+ when(mDockManager.isDocked()).thenReturn(true);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(DOZE_AOD_DOCKED);
+
+ mMachine.requestState(DOZE_PULSE_DONE);
+
+ verify(mPartMock, never()).transitionTo(DOZE_AOD_DOCKED, DOZE_PULSE_DONE);
+ }
+
+ @Test
public void testPulseDone_dozeSuppressed_afterDocked_goesToDoze() {
when(mHost.isDozeSuppressed()).thenReturn(true);
when(mDockManager.isDocked()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index 0bf0f04d2d43..425bf88ebec0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.pip;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
@@ -56,11 +57,15 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
private PipBoundsHandler mPipBoundsHandler;
private DisplayInfo mDefaultDisplayInfo;
+ private ComponentName mTestComponentName1;
+ private ComponentName mTestComponentName2;
@Before
public void setUp() throws Exception {
initializeMockResources();
mPipBoundsHandler = new PipBoundsHandler(mContext, new PipSnapAlgorithm(mContext));
+ mTestComponentName1 = new ComponentName(mContext, "component1");
+ mTestComponentName2 = new ComponentName(mContext, "component2");
mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo);
}
@@ -121,7 +126,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
};
for (float aspectRatio : aspectRatios) {
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+ mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
assertEquals("Destination bounds matches the given aspect ratio",
@@ -137,7 +142,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
};
for (float aspectRatio : invalidAspectRatios) {
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+ mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
assertEquals("Destination bounds fallbacks to default aspect ratio",
@@ -153,7 +158,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE);
+ mTestComponentName1, aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -177,7 +182,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
final float aspectRatio = aspectRatios[i];
final Size minimalSize = minimalSizes[i];
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize);
+ mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize);
assertTrue("Destination bounds is no smaller than minimal requirement",
(destinationBounds.width() == minimalSize.getWidth()
&& destinationBounds.height() >= minimalSize.getHeight())
@@ -198,7 +203,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2);
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, currentBounds, minSize);
+ mTestComponentName1, aspectRatio, currentBounds, minSize);
assertTrue("Destination bounds ignores minimal size",
destinationBounds.width() > minSize.getWidth()
@@ -206,81 +211,92 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
}
@Test
+ public void getDestinationBounds_withDifferentComponentName_ignoreLastPosition() {
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+
+ oldPosition.offset(0, -100);
+ mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition);
+
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName2,
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+
+ assertNonBoundsInclusionWithMargin("ignore saved bounds", oldPosition, newPosition);
+ }
+
+ @Test
public void setShelfHeight_offsetBounds() {
final int shelfHeight = 100;
- final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsHandler.setShelfHeight(true, shelfHeight);
- final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -shelfHeight);
- assertBoundsWithMargin("offsetBounds by shelf", oldPosition, newPosition);
+ assertBoundsInclusionWithMargin("offsetBounds by shelf", oldPosition, newPosition);
}
@Test
public void onImeVisibilityChanged_offsetBounds() {
final int imeHeight = 100;
- final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
- final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -imeHeight);
- assertBoundsWithMargin("offsetBounds by IME", oldPosition, newPosition);
+ assertBoundsInclusionWithMargin("offsetBounds by IME", oldPosition, newPosition);
}
@Test
public void onSaveReentryBounds_restoreLastPosition() {
- final ComponentName componentName = new ComponentName(mContext, "component1");
- final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -100);
- mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition);
+ mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition);
- final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
- assertBoundsWithMargin("restoreLastPosition", oldPosition, newPosition);
+ assertBoundsInclusionWithMargin("restoreLastPosition", oldPosition, newPosition);
}
@Test
public void onResetReentryBounds_useDefaultBounds() {
- final ComponentName componentName = new ComponentName(mContext, "component1");
- final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+ final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final Rect newBounds = new Rect(defaultBounds);
newBounds.offset(0, -100);
- mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
+ mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds);
- mPipBoundsHandler.onResetReentryBounds(componentName);
- final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+ mPipBoundsHandler.onResetReentryBounds(mTestComponentName1);
+ final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
- assertBoundsWithMargin("useDefaultBounds", defaultBounds, actualBounds);
+ assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds);
}
@Test
public void onResetReentryBounds_componentMismatch_restoreLastPosition() {
- final ComponentName componentName = new ComponentName(mContext, "component1");
- final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+ final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final Rect newBounds = new Rect(defaultBounds);
newBounds.offset(0, -100);
- mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
+ mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds);
- mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2"));
- final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+ mPipBoundsHandler.onResetReentryBounds(mTestComponentName2);
+ final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
- assertBoundsWithMargin("restoreLastPosition", newBounds, actualBounds);
+ assertBoundsInclusionWithMargin("restoreLastPosition", newBounds, actualBounds);
}
- private void assertBoundsWithMargin(String from, Rect expected, Rect actual) {
+ private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) {
final Rect expectedWithMargin = new Rect(expected);
expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
assertTrue(from + ": expect " + expected
@@ -288,4 +304,13 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
+ " with error margin " + ROUNDING_ERROR_MARGIN,
expectedWithMargin.contains(actual));
}
+
+ private void assertNonBoundsInclusionWithMargin(String from, Rect expected, Rect actual) {
+ final Rect expectedWithMargin = new Rect(expected);
+ expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
+ assertFalse(from + ": expect " + expected
+ + " not contains " + actual
+ + " with error margin " + ROUNDING_ERROR_MARGIN,
+ expectedWithMargin.contains(actual));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
index f1672b1c644d..f6b7b74d4bfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
@@ -106,6 +106,10 @@ class MagnetizedObjectTest : SysuiTestCase() {
location[1] = targetCenterY - targetSize / 2 // y = 800
}
}.`when`(targetView).getLocationOnScreen(ArgumentMatchers.any())
+ doAnswer { invocation ->
+ (invocation.arguments[0] as Runnable).run()
+ true
+ }.`when`(targetView).post(ArgumentMatchers.any())
`when`(targetView.context).thenReturn(context)
magneticTarget = MagnetizedObject.MagneticTarget(targetView, magneticFieldRadius)
@@ -408,6 +412,10 @@ class MagnetizedObjectTest : SysuiTestCase() {
`when`(secondTargetView.width).thenReturn(targetSize) // width = 200
`when`(secondTargetView.height).thenReturn(targetSize) // height = 200
doAnswer { invocation ->
+ (invocation.arguments[0] as Runnable).run()
+ true
+ }.`when`(secondTargetView).post(ArgumentMatchers.any())
+ doAnswer { invocation ->
(invocation.arguments[0] as IntArray).also { location ->
// Return the top left of the target.
location[0] = secondTargetCenterX - targetSize / 2 // x = 0
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
index 1a1c30d1d5f9..620261b375d2 100644
--- a/packages/Tethering/tests/integration/Android.bp
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -39,4 +39,9 @@ android_test {
"android.test.base",
"android.test.mock",
],
+ jni_libs: [
+ // For mockito extended
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
}
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index dbd68ef77cb7..b02bb23f9807 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -205,7 +205,7 @@ public class EthernetTetheringTest {
requestWithStaticIpv4(localAddr, clientAddr));
mTetheringEventCallback.awaitInterfaceTethered();
- assertInterfaceHasIpAddress(iface, clientAddr);
+ assertInterfaceHasIpAddress(iface, localAddr);
byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray();
byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray();
diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml
index 530bc0788a78..4ff1d3777f25 100644
--- a/packages/Tethering/tests/unit/AndroidManifest.xml
+++ b/packages/Tethering/tests/unit/AndroidManifest.xml
@@ -20,7 +20,16 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
+ <service
+ android:name="com.android.server.connectivity.tethering.MockTetheringService"
+ android:permission="android.permission.TETHER_PRIVILEGED"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.server.connectivity.tethering.TetheringService"/>
+ </intent-filter>
+ </service>
</application>
+
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.networkstack.tethering.tests.unit"
android:label="Tethering service tests">
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java
new file mode 100644
index 000000000000..355ece9a44a1
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.connectivity.tethering;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Intent;
+import android.net.ITetheringConnector;
+import android.os.Binder;
+import android.os.IBinder;
+
+public class MockTetheringService extends TetheringService {
+ private final Tethering mTethering = mock(Tethering.class);
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new MockTetheringConnector(super.onBind(intent));
+ }
+
+ @Override
+ public Tethering makeTethering(TetheringDependencies deps) {
+ return mTethering;
+ }
+
+ public Tethering getTethering() {
+ return mTethering;
+ }
+
+ public class MockTetheringConnector extends Binder {
+ final IBinder mBase;
+ MockTetheringConnector(IBinder base) {
+ mBase = base;
+ }
+
+ public ITetheringConnector getTetheringConnector() {
+ return ITetheringConnector.Stub.asInterface(mBase);
+ }
+
+ public MockTetheringService getService() {
+ return MockTetheringService.this;
+ }
+ }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java
new file mode 100644
index 000000000000..d9d3e73eb4e3
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.connectivity.tethering;
+
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.net.IIntResultListener;
+import android.net.ITetheringConnector;
+import android.net.ITetheringEventCallback;
+import android.net.TetheringRequestParcel;
+import android.os.ResultReceiver;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ServiceTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.connectivity.tethering.MockTetheringService.MockTetheringConnector;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class TetheringServiceTest {
+ private static final String TEST_IFACE_NAME = "test_wlan0";
+ private static final String TEST_CALLER_PKG = "test_pkg";
+ @Mock private ITetheringEventCallback mITetheringEventCallback;
+ @Rule public ServiceTestRule mServiceTestRule;
+ private Tethering mTethering;
+ private Intent mMockServiceIntent;
+ private ITetheringConnector mTetheringConnector;
+
+ private class TestTetheringResult extends IIntResultListener.Stub {
+ private int mResult = -1; // Default value that does not match any result code.
+ @Override
+ public void onResult(final int resultCode) {
+ mResult = resultCode;
+ }
+
+ public void assertResult(final int expected) {
+ assertEquals(expected, mResult);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mServiceTestRule = new ServiceTestRule();
+ mMockServiceIntent = new Intent(
+ InstrumentationRegistry.getTargetContext(),
+ MockTetheringService.class);
+ final MockTetheringConnector mockConnector =
+ (MockTetheringConnector) mServiceTestRule.bindService(mMockServiceIntent);
+ mTetheringConnector = mockConnector.getTetheringConnector();
+ final MockTetheringService service = mockConnector.getService();
+ mTethering = service.getTethering();
+ verify(mTethering).startStateMachineUpdaters();
+ when(mTethering.hasTetherableConfiguration()).thenReturn(true);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mServiceTestRule.unbindService();
+ }
+
+ @Test
+ public void testTether() throws Exception {
+ 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).tether(TEST_IFACE_NAME);
+ verifyNoMoreInteractions(mTethering);
+ result.assertResult(TETHER_ERROR_NO_ERROR);
+ }
+
+ @Test
+ public void testUntether() throws Exception {
+ 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).untether(TEST_IFACE_NAME);
+ verifyNoMoreInteractions(mTethering);
+ result.assertResult(TETHER_ERROR_NO_ERROR);
+ }
+
+ @Test
+ public void testSetUsbTethering() throws Exception {
+ 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).setUsbTethering(true /* enable */);
+ verifyNoMoreInteractions(mTethering);
+ result.assertResult(TETHER_ERROR_NO_ERROR);
+ }
+
+ @Test
+ public void testStartTethering() throws Exception {
+ final TestTetheringResult result = new TestTetheringResult();
+ final TetheringRequestParcel request = new TetheringRequestParcel();
+ request.tetheringType = TETHERING_WIFI;
+ mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result);
+ verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).startTethering(eq(request), eq(result));
+ verifyNoMoreInteractions(mTethering);
+ }
+
+ @Test
+ public void testStopTethering() throws Exception {
+ final TestTetheringResult result = new TestTetheringResult();
+ mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, result);
+ verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).stopTethering(TETHERING_WIFI);
+ verifyNoMoreInteractions(mTethering);
+ result.assertResult(TETHER_ERROR_NO_ERROR);
+ }
+
+ @Test
+ public void testRequestLatestTetheringEntitlementResult() throws Exception {
+ final ResultReceiver result = new ResultReceiver(null);
+ mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
+ true /* showEntitlementUi */, TEST_CALLER_PKG);
+ verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI),
+ eq(result), eq(true) /* showEntitlementUi */);
+ verifyNoMoreInteractions(mTethering);
+ }
+
+ @Test
+ public void testRegisterTetheringEventCallback() throws Exception {
+ mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback,
+ TEST_CALLER_PKG);
+ verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback));
+ verifyNoMoreInteractions(mTethering);
+ }
+
+ @Test
+ public void testUnregisterTetheringEventCallback() throws Exception {
+ mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback,
+ TEST_CALLER_PKG);
+ verify(mTethering).unregisterTetheringEventCallback(
+ eq(mITetheringEventCallback));
+ verifyNoMoreInteractions(mTethering);
+ }
+
+ @Test
+ public void testStopAllTethering() throws Exception {
+ final TestTetheringResult result = new TestTetheringResult();
+ mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, result);
+ verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).untetherAll();
+ verifyNoMoreInteractions(mTethering);
+ result.assertResult(TETHER_ERROR_NO_ERROR);
+ }
+
+ @Test
+ public void testIsTetheringSupported() throws Exception {
+ final TestTetheringResult result = new TestTetheringResult();
+ mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, result);
+ verify(mTethering).hasTetherableConfiguration();
+ verifyNoMoreInteractions(mTethering);
+ result.assertResult(TETHER_ERROR_NO_ERROR);
+ }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index a59c6fd9e193..3a580dd8e5bd 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -140,7 +140,9 @@ import com.android.networkstack.tethering.R;
import com.android.testutils.MiscAssertsKt;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -439,6 +441,18 @@ public class TetheringTest {
return buildMobileUpstreamState(false, true, true);
}
+ // See FakeSettingsProvider#clearSettingsProvider() that this needs to be called before and
+ // after use.
+ @BeforeClass
+ public static void setupOnce() {
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2100c1a8be44..7230b00f87ad 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -413,7 +413,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
&& component.getPackageName().equals(packageName))
|| userState.mCrashedServices.removeIf(component -> component != null
&& component.getPackageName().equals(packageName));
- if (reboundAService) {
+ // Reloads the installed services info to make sure the rebound service could
+ // get a new one.
+ userState.mInstalledServices.clear();
+ final boolean configurationChanged =
+ readConfigurationForUserStateLocked(userState);
+ if (reboundAService || configurationChanged) {
onUserStateChangedLocked(userState);
}
migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
new file mode 100644
index 000000000000..3612e093c8bd
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -0,0 +1,131 @@
+/*
+ * 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.autofill;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+
+/**
+ * Controls the interaction with the IME for the inline suggestion sessions.
+ */
+final class AutofillInlineSessionController {
+ @NonNull
+ private final InputMethodManagerInternal mInputMethodManagerInternal;
+ private final int mUserId;
+ @NonNull
+ private final ComponentName mComponentName;
+ @NonNull
+ private final Object mLock;
+ @NonNull
+ private final Handler mHandler;
+
+ @GuardedBy("mLock")
+ private AutofillInlineSuggestionsRequestSession mSession;
+
+ AutofillInlineSessionController(InputMethodManagerInternal inputMethodManagerInternal,
+ int userId, ComponentName componentName, Handler handler, Object lock) {
+ mInputMethodManagerInternal = inputMethodManagerInternal;
+ mUserId = userId;
+ mComponentName = componentName;
+ mHandler = handler;
+ mLock = lock;
+ }
+
+
+ /**
+ * 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.
+ */
+ @GuardedBy("mLock")
+ void onCreateInlineSuggestionsRequestLocked(@NonNull AutofillId autofillId,
+ @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) {
+ // TODO(b/151123764): rename the method to better reflect what it does.
+ if (mSession != null) {
+ // Send an empty response to IME and destroy the existing session.
+ mSession.onInlineSuggestionsResponseLocked(mSession.getAutofillIdLocked(),
+ new InlineSuggestionsResponse(Collections.EMPTY_LIST));
+ mSession.destroySessionLocked();
+ }
+ // TODO(b/151123764): consider reusing the same AutofillInlineSession object for the
+ // same field.
+ mSession = new AutofillInlineSuggestionsRequestSession(mInputMethodManagerInternal, mUserId,
+ mComponentName, mHandler, mLock, autofillId, requestConsumer, uiExtras);
+ mSession.onCreateInlineSuggestionsRequestLocked();
+
+ }
+
+ /**
+ * Returns the {@link InlineSuggestionsRequest} provided by IME for the last request.
+ *
+ * <p> The caller is responsible for making sure Autofill hears back from IME before calling
+ * this method, using the {@code requestConsumer} provided when calling {@link
+ * #onCreateInlineSuggestionsRequestLocked(AutofillId, Consumer, Bundle)}.
+ */
+ @GuardedBy("mLock")
+ Optional<InlineSuggestionsRequest> getInlineSuggestionsRequestLocked() {
+ if (mSession != null) {
+ return mSession.getInlineSuggestionsRequestLocked();
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Requests the IME to hide the current suggestions, if any. Returns true if the message is sent
+ * to the IME.
+ */
+ @GuardedBy("mLock")
+ boolean hideInlineSuggestionsUiLocked(@NonNull AutofillId autofillId) {
+ if (mSession != null) {
+ return mSession.onInlineSuggestionsResponseLocked(autofillId,
+ new InlineSuggestionsResponse(Collections.EMPTY_LIST));
+ }
+ return false;
+ }
+
+ /**
+ * Requests showing the inline suggestion in the IME when the IME becomes visible and is focused
+ * on the {@code autofillId}.
+ *
+ * @return false if there is no session, or if the IME callback is not available in the session.
+ */
+ @GuardedBy("mLock")
+ boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
+ @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
+ // TODO(b/151123764): rename the method to better reflect what it does.
+ if (mSession != null) {
+ return mSession.onInlineSuggestionsResponseLocked(autofillId,
+ inlineSuggestionsResponse);
+ }
+ return false;
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
new file mode 100644
index 000000000000..ca230b6936ff
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -0,0 +1,419 @@
+/*
+ * 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.autofill;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.annotation.BinderThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.lang.ref.WeakReference;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Maintains an inline suggestion session with the IME.
+ *
+ * <p> Each session corresponds to one request from the Autofill manager service to create an
+ * {@link InlineSuggestionsRequest}. It's responsible for receiving callbacks from the IME and
+ * sending {@link android.view.inputmethod.InlineSuggestionsResponse} to IME.
+ */
+final class AutofillInlineSuggestionsRequestSession {
+
+ private static final String TAG = AutofillInlineSuggestionsRequestSession.class.getSimpleName();
+ private static final int INLINE_REQUEST_TIMEOUT_MS = 200;
+
+ @NonNull
+ private final InputMethodManagerInternal mInputMethodManagerInternal;
+ private final int mUserId;
+ @NonNull
+ private final ComponentName mComponentName;
+ @NonNull
+ private final Object mLock;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final Bundle mUiExtras;
+
+ @GuardedBy("mLock")
+ @NonNull
+ private AutofillId mAutofillId;
+ @GuardedBy("mLock")
+ @Nullable
+ private Consumer<InlineSuggestionsRequest> mImeRequestConsumer;
+
+ @GuardedBy("mLock")
+ private boolean mImeRequestReceived;
+ @GuardedBy("mLock")
+ @Nullable
+ private InlineSuggestionsRequest mImeRequest;
+ @GuardedBy("mLock")
+ @Nullable
+ private IInlineSuggestionsResponseCallback mResponseCallback;
+ @GuardedBy("mLock")
+ @Nullable
+ private Runnable mTimeoutCallback;
+
+ @GuardedBy("mLock")
+ @Nullable
+ private AutofillId mImeCurrentFieldId;
+ @GuardedBy("mLock")
+ private boolean mImeInputStarted;
+ @GuardedBy("mLock")
+ private boolean mImeInputViewStarted;
+ @GuardedBy("mLock")
+ @Nullable
+ private InlineSuggestionsResponse mInlineSuggestionsResponse;
+ @GuardedBy("mLock")
+ private boolean mPreviousResponseIsNotEmpty;
+
+ @GuardedBy("mLock")
+ private boolean mDestroyed = false;
+
+ AutofillInlineSuggestionsRequestSession(
+ @NonNull InputMethodManagerInternal inputMethodManagerInternal, int userId,
+ @NonNull ComponentName componentName, @NonNull Handler handler, @NonNull Object lock,
+ @NonNull AutofillId autofillId,
+ @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) {
+ mInputMethodManagerInternal = inputMethodManagerInternal;
+ mUserId = userId;
+ mComponentName = componentName;
+ mHandler = handler;
+ mLock = lock;
+ mUiExtras = uiExtras;
+
+ mAutofillId = autofillId;
+ mImeRequestConsumer = requestConsumer;
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ AutofillId getAutofillIdLocked() {
+ return mAutofillId;
+ }
+
+ /**
+ * Returns the {@link InlineSuggestionsRequest} provided by IME.
+ *
+ * <p> The caller is responsible for making sure Autofill hears back from IME before calling
+ * this method, using the {@link #mImeRequestConsumer}.
+ */
+ @GuardedBy("mLock")
+ Optional<InlineSuggestionsRequest> getInlineSuggestionsRequestLocked() {
+ if (mDestroyed) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(mImeRequest);
+ }
+
+ /**
+ * Requests showing the inline suggestion in the IME when the IME becomes visible and is focused
+ * on the {@code autofillId}.
+ *
+ * @return false if the IME callback is not available.
+ */
+ @GuardedBy("mLock")
+ boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
+ @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
+ if (mDestroyed) {
+ return false;
+ }
+ if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked called for:" + autofillId);
+ if (mImeRequest == null || mResponseCallback == null) {
+ return false;
+ }
+ // TODO(b/151123764): each session should only correspond to one field.
+ mAutofillId = autofillId;
+ mInlineSuggestionsResponse = inlineSuggestionsResponse;
+ maybeUpdateResponseToImeLocked();
+ return true;
+ }
+
+ /**
+ * This method must be called when the session is destroyed, to avoid further callbacks from/to
+ * the IME.
+ */
+ @GuardedBy("mLock")
+ void destroySessionLocked() {
+ mDestroyed = true;
+ }
+
+ /**
+ * Requests the IME to create an {@link InlineSuggestionsRequest}.
+ *
+ * <p> This method should only be called once per session.
+ */
+ @GuardedBy("mLock")
+ void onCreateInlineSuggestionsRequestLocked() {
+ if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId);
+ if (mDestroyed) {
+ return;
+ }
+ mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId,
+ new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras),
+ new InlineSuggestionsRequestCallbackImpl(this));
+ mTimeoutCallback = () -> {
+ Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
+ handleOnReceiveImeRequest(null, null);
+ };
+ mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS);
+ }
+
+ /**
+ * Optionally sends inline response to the IME, depending on the current state.
+ */
+ @GuardedBy("mLock")
+ private void maybeUpdateResponseToImeLocked() {
+ if (sDebug) Log.d(TAG, "maybeUpdateResponseToImeLocked called");
+ 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.
+ // 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.
+
+ // TODO(b/149945531): clear the existing suggestions when IME is hide, once the bug is
+ // fixed.
+ //if (sDebug) Log.d(TAG, "Send empty inline response");
+ //updateResponseToImeUncheckLocked(new InlineSuggestionsResponse(Collections
+ // .EMPTY_LIST));
+ //mPreviousResponseIsNotEmpty = false;
+ } else if (mImeInputViewStarted && mInlineSuggestionsResponse != null && match(mAutofillId,
+ mImeCurrentFieldId)) {
+ // 2. if IME is visible, and response is not null, send the response
+ boolean isEmptyResponse = mInlineSuggestionsResponse.getInlineSuggestions().isEmpty();
+ if (isEmptyResponse && !mPreviousResponseIsNotEmpty) {
+ // No-op if both the previous response and current response are empty.
+ return;
+ }
+ if (sDebug) {
+ Log.d(TAG, "Send inline response: "
+ + mInlineSuggestionsResponse.getInlineSuggestions().size());
+ }
+ updateResponseToImeUncheckLocked(mInlineSuggestionsResponse);
+ // TODO(b/149945531): don't set the response to null so it's cached, once the bug is
+ // fixed.
+ mInlineSuggestionsResponse = null;
+ mPreviousResponseIsNotEmpty = !isEmptyResponse;
+ }
+ }
+
+ /**
+ * Sends the {@code response} to the IME, assuming all the relevant checks are already done.
+ */
+ @GuardedBy("mLock")
+ private void updateResponseToImeUncheckLocked(InlineSuggestionsResponse response) {
+ if (mDestroyed) {
+ return;
+ }
+ try {
+ mResponseCallback.onInlineSuggestionsResponse(mAutofillId, response);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME");
+ }
+ }
+
+ /**
+ * Handles the {@code request} and {@code callback} received from the IME.
+ *
+ * <p> Should only invoked in the {@link #mHandler} thread.
+ */
+ private void handleOnReceiveImeRequest(@Nullable InlineSuggestionsRequest request,
+ @Nullable IInlineSuggestionsResponseCallback callback) {
+ synchronized (mLock) {
+ if (mDestroyed || mImeRequestReceived) {
+ return;
+ }
+ mImeRequestReceived = true;
+
+ if (mTimeoutCallback != null) {
+ if (sDebug) Log.d(TAG, "removing timeout callback");
+ mHandler.removeCallbacks(mTimeoutCallback);
+ mTimeoutCallback = null;
+ }
+ if (request != null && callback != null) {
+ mImeRequest = request;
+ mResponseCallback = callback;
+ handleOnReceiveImeStatusUpdated(mAutofillId, true, false);
+ }
+ if (mImeRequestConsumer != null) {
+ // Note that mImeRequest is only set if both request and callback are non-null.
+ mImeRequestConsumer.accept(mImeRequest);
+ mImeRequestConsumer = null;
+ }
+ }
+ }
+
+ /**
+ * Handles the IME status updates received from the IME.
+ *
+ * <p> Should only be invoked in the {@link #mHandler} thread.
+ */
+ private void handleOnReceiveImeStatusUpdated(boolean imeInputStarted,
+ boolean imeInputViewStarted) {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ if (mImeCurrentFieldId != null) {
+ boolean imeInputStartedChanged = (mImeInputStarted != imeInputStarted);
+ boolean imeInputViewStartedChanged = (mImeInputViewStarted != imeInputViewStarted);
+ mImeInputStarted = imeInputStarted;
+ mImeInputViewStarted = imeInputViewStarted;
+ if (imeInputStartedChanged || imeInputViewStartedChanged) {
+ maybeUpdateResponseToImeLocked();
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles the IME status updates received from the IME.
+ *
+ * <p> Should only be invoked in the {@link #mHandler} thread.
+ */
+ private void handleOnReceiveImeStatusUpdated(@Nullable AutofillId imeFieldId,
+ boolean imeInputStarted, boolean imeInputViewStarted) {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ if (imeFieldId != null) {
+ mImeCurrentFieldId = imeFieldId;
+ }
+ handleOnReceiveImeStatusUpdated(imeInputStarted, imeInputViewStarted);
+ }
+ }
+
+ private static final class InlineSuggestionsRequestCallbackImpl extends
+ IInlineSuggestionsRequestCallback.Stub {
+
+ private final WeakReference<AutofillInlineSuggestionsRequestSession> mSession;
+
+ private InlineSuggestionsRequestCallbackImpl(
+ AutofillInlineSuggestionsRequestSession session) {
+ mSession = new WeakReference<>(session);
+ }
+
+ @BinderThread
+ @Override
+ public void onInlineSuggestionsUnsupported() throws RemoteException {
+ if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeRequest, session,
+ null, null));
+ }
+ }
+
+ @BinderThread
+ @Override
+ public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+ IInlineSuggestionsResponseCallback callback) {
+ if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeRequest, session,
+ request, callback));
+ }
+ }
+
+ @Override
+ public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
+ if (sDebug) Log.d(TAG, "onInputMethodStartInput() received on " + imeFieldId);
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+ session, imeFieldId, true, false));
+ }
+ }
+
+ @Override
+ public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
+ if (sDebug) {
+ Log.d(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
+ }
+ }
+
+ @BinderThread
+ @Override
+ public void onInputMethodStartInputView() {
+ if (sDebug) Log.d(TAG, "onInputMethodStartInputView() received");
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+ session, true, true));
+ }
+ }
+
+ @BinderThread
+ @Override
+ public void onInputMethodFinishInputView() {
+ if (sDebug) Log.d(TAG, "onInputMethodFinishInputView() received");
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+ session, true, false));
+ }
+ }
+
+ @Override
+ public void onInputMethodFinishInput() throws RemoteException {
+ if (sDebug) Log.d(TAG, "onInputMethodFinishInput() received");
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+ session, false, false));
+ }
+ }
+ }
+
+ private static boolean match(@Nullable AutofillId autofillId,
+ @Nullable AutofillId imeClientFieldId) {
+ // The IME doesn't have information about the virtual view id for the child views in the
+ // web view, so we are only comparing the parent view id here. This means that for cases
+ // where there are two input fields in the web view, they will have the same view id
+ // (although different virtual child id), and we will not be able to distinguish them.
+ return autofillId != null && imeClientFieldId != null
+ && autofillId.getViewId() == imeClientFieldId.getViewId();
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
deleted file mode 100644
index e2d511212a71..000000000000
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.autofill;
-
-import static com.android.server.autofill.Helper.sDebug;
-
-import android.annotation.BinderThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-import android.view.autofill.AutofillId;
-import android.view.inputmethod.InlineSuggestionsRequest;
-import android.view.inputmethod.InlineSuggestionsResponse;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInlineSuggestionsResponseCallback;
-import com.android.internal.view.InlineSuggestionsRequestInfo;
-import com.android.server.inputmethod.InputMethodManagerInternal;
-
-import java.util.Collections;
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-
-/**
- * Maintains an autofill inline suggestion session that communicates with the IME.
- *
- * <p>
- * The same session may be reused for multiple input fields involved in the same autofill
- * {@link Session}. Therefore, one {@link InlineSuggestionsRequest} and one
- * {@link IInlineSuggestionsResponseCallback} may be used to generate and callback with inline
- * suggestions for different input fields.
- *
- * <p>
- * This class is the sole place in Autofill responsible for directly communicating with the IME. It
- * receives the IME input view start/finish events, with the associated IME field Id. It uses the
- * information to decide when to send the {@link InlineSuggestionsResponse} to IME. As a result,
- * some of the response will be cached locally and only be sent when the IME is ready to show them.
- *
- * <p>
- * See {@link android.inputmethodservice.InlineSuggestionSession} comments for InputMethodService
- * side flow.
- *
- * <p>
- * This class should hold the same lock as {@link Session} as they call into each other.
- */
-final class InlineSuggestionSession {
-
- private static final String TAG = "AfInlineSuggestionSession";
- private static final int INLINE_REQUEST_TIMEOUT_MS = 200;
-
- @NonNull
- private final InputMethodManagerInternal mInputMethodManagerInternal;
- private final int mUserId;
- @NonNull
- private final ComponentName mComponentName;
- @NonNull
- private final Object mLock;
- @NonNull
- private final ImeStatusListener mImeStatusListener;
- @NonNull
- private final Handler mHandler;
-
- /**
- * To avoid the race condition, one should not access {@code mPendingImeResponse} without
- * holding the {@code mLock}. For consuming the existing value, tt's recommended to use
- * {@link #getPendingImeResponse()} to get a copy of the reference to avoid blocking call.
- */
- @GuardedBy("mLock")
- @Nullable
- private CompletableFuture<ImeResponse> mPendingImeResponse;
-
- @GuardedBy("mLock")
- @Nullable
- private AutofillResponse mPendingAutofillResponse;
-
- @GuardedBy("mLock")
- private boolean mIsLastResponseNonEmpty = false;
-
- @Nullable
- @GuardedBy("mLock")
- private AutofillId mImeFieldId = null;
-
- @GuardedBy("mLock")
- private boolean mImeInputViewStarted = false;
-
- InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
- int userId, ComponentName componentName, Handler handler, Object lock) {
- mInputMethodManagerInternal = inputMethodManagerInternal;
- mUserId = userId;
- mComponentName = componentName;
- mHandler = handler;
- mLock = lock;
- mImeStatusListener = new ImeStatusListener() {
- @Override
- public void onInputMethodStartInput(AutofillId imeFieldId) {
- synchronized (mLock) {
- mImeFieldId = imeFieldId;
- mImeInputViewStarted = false;
- }
- }
-
- @Override
- public void onInputMethodStartInputView() {
- synchronized (mLock) {
- mImeInputViewStarted = true;
- AutofillResponse pendingAutofillResponse = mPendingAutofillResponse;
- if (pendingAutofillResponse != null
- && pendingAutofillResponse.mAutofillId.equalsIgnoreSession(
- mImeFieldId)) {
- mPendingAutofillResponse = null;
- onInlineSuggestionsResponseLocked(pendingAutofillResponse.mAutofillId,
- pendingAutofillResponse.mResponse);
- }
- }
- }
-
- @Override
- public void onInputMethodFinishInputView() {
- synchronized (mLock) {
- mImeInputViewStarted = false;
- }
- }
-
- @Override
- public void onInputMethodFinishInput() {
- synchronized (mLock) {
- mImeFieldId = null;
- }
- }
- };
- }
-
- public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId,
- @NonNull Consumer<InlineSuggestionsRequest> requestConsumer) {
- if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequest called for " + autofillId);
-
- synchronized (mLock) {
- // Clean up all the state about the previous request.
- hideInlineSuggestionsUi(autofillId);
- mImeFieldId = null;
- mImeInputViewStarted = false;
- if (mPendingImeResponse != null && !mPendingImeResponse.isDone()) {
- mPendingImeResponse.complete(null);
- }
- mPendingImeResponse = new CompletableFuture<>();
- // TODO(b/146454892): pipe the uiExtras from the ExtServices.
- mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
- mUserId,
- new InlineSuggestionsRequestInfo(mComponentName, autofillId, new Bundle()),
- new InlineSuggestionsRequestCallbackImpl(autofillId, mPendingImeResponse,
- mImeStatusListener, requestConsumer, mHandler, mLock));
- }
- }
-
- public Optional<InlineSuggestionsRequest> getInlineSuggestionsRequest() {
- final CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
- if (pendingImeResponse == null || !pendingImeResponse.isDone()) {
- return Optional.empty();
- }
- return Optional.ofNullable(pendingImeResponse.getNow(null)).map(ImeResponse::getRequest);
- }
-
- public boolean hideInlineSuggestionsUi(@NonNull AutofillId autofillId) {
- synchronized (mLock) {
- if (mIsLastResponseNonEmpty) {
- return onInlineSuggestionsResponseLocked(autofillId,
- new InlineSuggestionsResponse(Collections.EMPTY_LIST));
- }
- return false;
- }
- }
-
- public boolean onInlineSuggestionsResponse(@NonNull AutofillId autofillId,
- @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
- synchronized (mLock) {
- return onInlineSuggestionsResponseLocked(autofillId, inlineSuggestionsResponse);
- }
- }
-
- private boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
- @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
- final CompletableFuture<ImeResponse> completedImsResponse = getPendingImeResponse();
- if (completedImsResponse == null || !completedImsResponse.isDone()) {
- if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked without IMS request");
- return false;
- }
- // There is no need to wait on the CompletableFuture since it should have been completed.
- ImeResponse imeResponse = completedImsResponse.getNow(null);
- if (imeResponse == null) {
- if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked with pending IMS response");
- return false;
- }
-
- // TODO(b/151846600): IME doesn't have access to the virtual id of the webview, so we
- // only compare the view id for now.
- if (!mImeInputViewStarted || mImeFieldId == null
- || autofillId.getViewId() != mImeFieldId.getViewId()) {
- if (sDebug) {
- Log.d(TAG,
- "onInlineSuggestionsResponseLocked not sent because input view is not "
- + "started for " + autofillId);
- }
- mPendingAutofillResponse = new AutofillResponse(autofillId, inlineSuggestionsResponse);
- // TODO(b/149442582): Although we are not sending the response to IME right away, we
- // still return true to indicate that the response may be sent eventually, such that
- // the dropdown UI will not be shown. This may not be the desired behavior in the
- // auto-focus case where IME isn't shown after switching back to an activity. We may
- // revisit this.
- return true;
- }
-
- try {
- imeResponse.mCallback.onInlineSuggestionsResponse(autofillId,
- inlineSuggestionsResponse);
- mIsLastResponseNonEmpty = !inlineSuggestionsResponse.getInlineSuggestions().isEmpty();
- if (sDebug) {
- Log.d(TAG, "Autofill sends inline response to IME: "
- + inlineSuggestionsResponse.getInlineSuggestions().size());
- }
- return true;
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME");
- return false;
- }
- }
-
- @Nullable
- @GuardedBy("mLock")
- private CompletableFuture<ImeResponse> getPendingImeResponse() {
- synchronized (mLock) {
- return mPendingImeResponse;
- }
- }
-
- private static final class InlineSuggestionsRequestCallbackImpl
- extends IInlineSuggestionsRequestCallback.Stub {
-
- private final Object mLock;
- private final AutofillId mAutofillId;
- @GuardedBy("mLock")
- private final CompletableFuture<ImeResponse> mResponse;
- @GuardedBy("mLock")
- private final Consumer<InlineSuggestionsRequest> mRequestConsumer;
- private final ImeStatusListener mImeStatusListener;
- private final Handler mHandler;
- private final Runnable mTimeoutCallback;
-
- private InlineSuggestionsRequestCallbackImpl(AutofillId autofillId,
- CompletableFuture<ImeResponse> response,
- ImeStatusListener imeStatusListener,
- Consumer<InlineSuggestionsRequest> requestConsumer,
- Handler handler, Object lock) {
- mAutofillId = autofillId;
- mResponse = response;
- mImeStatusListener = imeStatusListener;
- mRequestConsumer = requestConsumer;
- mLock = lock;
-
- mHandler = handler;
- mTimeoutCallback = () -> {
- Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
- completeIfNot(null);
- };
- mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS);
- }
-
- private void completeIfNot(@Nullable ImeResponse response) {
- synchronized (mLock) {
- if (mResponse.isDone()) {
- return;
- }
- mResponse.complete(response);
- mRequestConsumer.accept(response == null ? null : response.mRequest);
- mHandler.removeCallbacks(mTimeoutCallback);
- }
- }
-
- @BinderThread
- @Override
- public void onInlineSuggestionsUnsupported() throws RemoteException {
- if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
- completeIfNot(null);
- }
-
- @BinderThread
- @Override
- public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
- IInlineSuggestionsResponseCallback callback) {
- if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
- mImeStatusListener.onInputMethodStartInput(mAutofillId);
- if (request != null && callback != null) {
- completeIfNot(new ImeResponse(request, callback));
- } else {
- completeIfNot(null);
- }
- }
-
- @Override
- public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
- if (sDebug) Log.d(TAG, "onInputMethodStartInput() received on " + imeFieldId);
- mImeStatusListener.onInputMethodStartInput(imeFieldId);
- }
-
- @Override
- public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
- if (sDebug) {
- Log.d(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
- }
- // TODO(b/151123764): use this signal to adjust the timeout on Autofill side waiting for
- // IME to show.
- }
-
- @BinderThread
- @Override
- public void onInputMethodStartInputView() {
- if (sDebug) Log.d(TAG, "onInputMethodStartInputView() received");
- mImeStatusListener.onInputMethodStartInputView();
- }
-
- @BinderThread
- @Override
- public void onInputMethodFinishInputView() {
- if (sDebug) Log.d(TAG, "onInputMethodFinishInputView() received");
- mImeStatusListener.onInputMethodFinishInputView();
- }
-
- @Override
- public void onInputMethodFinishInput() throws RemoteException {
- if (sDebug) Log.d(TAG, "onInputMethodFinishInput() received");
- mImeStatusListener.onInputMethodFinishInput();
- }
- }
-
- private interface ImeStatusListener {
- void onInputMethodStartInput(AutofillId imeFieldId);
-
- void onInputMethodStartInputView();
-
- void onInputMethodFinishInputView();
-
- void onInputMethodFinishInput();
- }
-
- /**
- * A data class wrapping Autofill responses for the inline suggestion request.
- */
- private static class AutofillResponse {
- @NonNull
- final AutofillId mAutofillId;
-
- @NonNull
- final InlineSuggestionsResponse mResponse;
-
- AutofillResponse(@NonNull AutofillId autofillId,
- @NonNull InlineSuggestionsResponse response) {
- mAutofillId = autofillId;
- mResponse = response;
- }
-
- }
-
- /**
- * A data class wrapping IME responses for the create inline suggestions request.
- */
- private static class ImeResponse {
- @NonNull
- final InlineSuggestionsRequest mRequest;
-
- @NonNull
- final IInlineSuggestionsResponseCallback mCallback;
-
- ImeResponse(@NonNull InlineSuggestionsRequest request,
- @NonNull IInlineSuggestionsResponseCallback callback) {
- mRequest = request;
- mCallback = callback;
- }
-
- InlineSuggestionsRequest getRequest() {
- return mRequest;
- }
- }
-}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 12905696ff98..4ecffd8d29b0 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -304,7 +304,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private boolean mForAugmentedAutofillOnly;
@Nullable
- private final InlineSuggestionSession mInlineSuggestionSession;
+ private final AutofillInlineSessionController mInlineSessionController;
/**
* Receiver of assist data from the app's {@link Activity}.
@@ -720,8 +720,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true);
if (inlineSuggestionsRequestConsumer != null) {
- mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
- inlineSuggestionsRequestConsumer);
+ // TODO(b/146454892): pipe the uiExtras from the ExtServices.
+ mInlineSessionController.onCreateInlineSuggestionsRequestLocked(mCurrentViewId,
+ inlineSuggestionsRequestConsumer, Bundle.EMPTY);
}
} else {
mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false);
@@ -777,8 +778,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mForAugmentedAutofillOnly = forAugmentedAutofillOnly;
setClientLocked(client);
- mInlineSuggestionSession = new InlineSuggestionSession(inputMethodManagerInternal, userId,
- componentName, handler, mLock);
+ mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal,
+ userId, componentName, handler, mLock);
mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
@@ -2561,7 +2562,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sVerbose) Slog.v(TAG, "Exiting view " + id);
mUi.hideFillUi(this);
hideAugmentedAutofillLocked(viewState);
- mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId);
+ mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
mCurrentViewId = null;
}
break;
@@ -2779,7 +2780,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
@Nullable String filterText) {
final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
- mInlineSuggestionSession.getInlineSuggestionsRequest();
+ mInlineSessionController.getInlineSuggestionsRequestLocked();
if (!inlineSuggestionsRequest.isPresent()) {
Log.w(TAG, "InlineSuggestionsRequest unavailable");
return false;
@@ -2801,7 +2802,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
inlineSuggestionsRequest.get(), response, filterText, mCurrentViewId,
this, () -> {
synchronized (mLock) {
- mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId);
+ mInlineSessionController.hideInlineSuggestionsUiLocked(
+ mCurrentViewId);
}
}, remoteRenderService);
if (inlineSuggestionsResponse == null) {
@@ -2809,7 +2811,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return false;
}
- return mInlineSuggestionSession.onInlineSuggestionsResponse(mCurrentViewId,
+ return mInlineSessionController.onInlineSuggestionsResponseLocked(mCurrentViewId,
inlineSuggestionsResponse);
}
@@ -3106,8 +3108,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
focusedId,
currentValue, inlineSuggestionsRequest,
/*inlineSuggestionsCallback=*/
- response -> mInlineSuggestionSession.onInlineSuggestionsResponse(
- mCurrentViewId, response),
+ response -> {
+ synchronized (mLock) {
+ return mInlineSessionController
+ .onInlineSuggestionsResponseLocked(
+ mCurrentViewId, response);
+ }
+ },
/*onErrorCallback=*/ () -> {
synchronized (mLock) {
cancelAugmentedAutofillLocked();
@@ -3125,11 +3132,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
&& (mForAugmentedAutofillOnly
|| !isInlineSuggestionsEnabledByAutofillProviderLocked())) {
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
- mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
- /*requestConsumer=*/ requestAugmentedAutofill);
+ // TODO(b/146454892): pipe the uiExtras from the ExtServices.
+ mInlineSessionController.onCreateInlineSuggestionsRequestLocked(mCurrentViewId,
+ /*requestConsumer=*/ requestAugmentedAutofill, Bundle.EMPTY);
} else {
requestAugmentedAutofill.accept(
- mInlineSuggestionSession.getInlineSuggestionsRequest().orElse(null));
+ mInlineSessionController.getInlineSuggestionsRequestLocked().orElse(null));
}
if (mAugmentedAutofillDestroyer == null) {
mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 192ea72224b1..3c0d880916ee 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -119,8 +119,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private static final int MESSAGE_DISABLE = 2;
private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3;
private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4;
- private static final int MESSAGE_REGISTER_ADAPTER = 20;
- private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
@@ -642,10 +640,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
Slog.w(TAG, "Callback is null in registerAdapter");
return null;
}
- Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
- msg.obj = callback;
- mHandler.sendMessage(msg);
-
+ synchronized (mCallbacks) {
+ mCallbacks.register(callback);
+ }
return mBluetooth;
}
@@ -655,9 +652,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return;
}
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
- msg.obj = callback;
- mHandler.sendMessage(msg);
+ synchronized (mCallbacks) {
+ mCallbacks.unregister(callback);
+ }
}
public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
@@ -1559,18 +1556,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
* Inform BluetoothAdapter instances that Adapter service is up
*/
private void sendBluetoothServiceUpCallback() {
- try {
- int n = mCallbacks.beginBroadcast();
- Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
- for (int i = 0; i < n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+ synchronized (mCallbacks) {
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+ for (int i = 0; i < n; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+ }
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
- } finally {
- mCallbacks.finishBroadcast();
}
}
@@ -1578,18 +1577,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
* Inform BluetoothAdapter instances that Adapter service is down
*/
private void sendBluetoothServiceDownCallback() {
- try {
- int n = mCallbacks.beginBroadcast();
- Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
- for (int i = 0; i < n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+ synchronized (mCallbacks) {
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+ for (int i = 0; i < n; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+ }
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
- } finally {
- mCallbacks.finishBroadcast();
}
}
@@ -1917,17 +1918,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mContext.getPackageName());
}
break;
-
- case MESSAGE_REGISTER_ADAPTER: {
- IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
- mCallbacks.register(callback);
- break;
- }
- case MESSAGE_UNREGISTER_ADAPTER: {
- IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
- mCallbacks.unregister(callback);
- break;
- }
case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: {
IBluetoothStateChangeCallback callback =
(IBluetoothStateChangeCallback) msg.obj;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 067147703b0c..9aefc8dc78ec 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4468,9 +4468,8 @@ class StorageManagerService extends IStorageManager.Stub
String.format("/storage/emulated/%d/Android/data/%s/",
userId, pkg);
- int appUid =
- UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
// Create package obb and data dir if it doesn't exist.
+ int appUid = UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
File file = new File(packageObbDir);
if (!file.exists()) {
vold.setupAppDir(packageObbDir, appUid);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 595275d20154..786e9cf66bfa 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -97,6 +97,7 @@ import android.os.storage.StorageManagerInternal;
import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -137,6 +138,7 @@ import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Activity manager code dealing with processes.
@@ -1846,11 +1848,13 @@ public final class ProcessList {
runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
}
- // Enable heap pointer tagging, unless disabled by the app manifest, target sdk level,
- // or the compat feature.
- if (app.info.allowsNativeHeapPointerTagging()
- && mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
- runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ if (Zygote.nativeSupportsTaggedPointers()) {
+ // Enable heap pointer tagging if supported by the kernel, unless disabled by the
+ // app manifest, target sdk level, or compat feature.
+ if (app.info.allowsNativeHeapPointerTagging()
+ && mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
+ runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ }
}
runtimeFlags |= decideGwpAsanLevel(app);
@@ -2127,18 +2131,11 @@ public final class ProcessList {
for (String packageName : packages) {
String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid();
long inode = pmInt.getCeDataInode(packageName, userId);
- if (inode != 0) {
- result.put(packageName, Pair.create(volumeUuid, inode));
- }
- }
- if (mAppDataIsolationWhitelistedApps != null) {
- for (String packageName : mAppDataIsolationWhitelistedApps) {
- String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid();
- long inode = pmInt.getCeDataInode(packageName, userId);
- if (inode != 0) {
- result.put(packageName, Pair.create(volumeUuid, inode));
- }
+ if (inode == 0) {
+ Slog.w(TAG, packageName + " inode == 0 (b/152760674)");
+ return null;
}
+ result.put(packageName, Pair.create(volumeUuid, inode));
}
return result;
@@ -2160,35 +2157,56 @@ public final class ProcessList {
app.setHasForegroundActivities(true);
}
- StorageManagerInternal storageManagerInternal = LocalServices.getService(
- StorageManagerInternal.class);
final Map<String, Pair<String, Long>> pkgDataInfoMap;
+ final Map<String, Pair<String, Long>> whitelistedAppDataInfoMap;
boolean bindMountAppStorageDirs = false;
+ boolean bindMountAppsData = mAppDataIsolationEnabled
+ && UserHandle.isApp(app.uid)
+ && mPlatformCompat.isChangeEnabled(APP_DATA_DIRECTORY_ISOLATION, app.info);
- if (mAppDataIsolationEnabled && UserHandle.isApp(app.uid)
- && mPlatformCompat.isChangeEnabled(APP_DATA_DIRECTORY_ISOLATION, app.info)) {
- // Get all packages belongs to the same shared uid. sharedPackages is empty array
- // if it doesn't have shared uid.
- final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
- final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
- app.info.packageName, app.userId);
- pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0
- ? new String[]{app.info.packageName} : sharedPackages, uid);
-
- int userId = UserHandle.getUserId(uid);
- if (mVoldAppDataIsolationEnabled
- && !storageManagerInternal.isExternalStorageService(uid)) {
- bindMountAppStorageDirs = true;
- if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
- app.processName)) {
- // Cannot prepare Android/app and Android/obb directory,
- // so we won't mount it in zygote.
- app.bindMountPending = true;
- bindMountAppStorageDirs = false;
- }
+ // Get all packages belongs to the same shared uid. sharedPackages is empty array
+ // if it doesn't have shared uid.
+ final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
+ final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
+ app.info.packageName, app.userId);
+ final String[] targetPackagesList = sharedPackages.length == 0
+ ? new String[]{app.info.packageName} : sharedPackages;
+
+ pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, targetPackagesList, uid);
+ if (pkgDataInfoMap == null) {
+ // TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
+ // tmp free pass.
+ bindMountAppsData = false;
+ }
+
+ // Remove all packages in pkgDataInfoMap from mAppDataIsolationWhitelistedApps, so
+ // it won't be mounted twice.
+ final Set<String> whitelistedApps = new ArraySet<>(mAppDataIsolationWhitelistedApps);
+ for (String pkg : targetPackagesList) {
+ whitelistedApps.remove(pkg);
+ }
+
+ whitelistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt,
+ whitelistedApps.toArray(new String[0]), uid);
+ if (whitelistedAppDataInfoMap == null) {
+ // TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
+ // tmp free pass.
+ bindMountAppsData = false;
+ }
+
+ int userId = UserHandle.getUserId(uid);
+ StorageManagerInternal storageManagerInternal = LocalServices.getService(
+ StorageManagerInternal.class);
+ if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid)
+ && !storageManagerInternal.isExternalStorageService(uid)) {
+ bindMountAppStorageDirs = true;
+ if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
+ app.processName)) {
+ // Cannot prepare Android/app and Android/obb directory,
+ // so we won't mount it in zygote.
+ app.bindMountPending = true;
+ bindMountAppStorageDirs = false;
}
- } else {
- pkgDataInfoMap = null;
}
final Process.ProcessStartResult startResult;
@@ -2206,7 +2224,8 @@ public final class ProcessList {
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
- app.mDisabledCompatChanges, pkgDataInfoMap, bindMountAppStorageDirs,
+ app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
+ bindMountAppsData, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
@@ -2214,7 +2233,7 @@ public final class ProcessList {
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
- bindMountAppStorageDirs,
+ whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
}
checkSlow(startTime, "startProcess: returned from zygote!");
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index ad3c8a61182f..d8acf0e331af 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -809,11 +809,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
locationRequest.setProvider(provider);
// Ignore location settings if in emergency mode. This is only allowed for
- // isUserEmergency request (introduced in HAL v2.0), or DBH request in HAL v1.1.
+ // isUserEmergency request (introduced in HAL v2.0), or HAL v1.1.
if (mNIHandler.getInEmergency()) {
GnssConfiguration.HalInterfaceVersion halVersion =
mGnssConfiguration.getHalInterfaceVersion();
- if (isUserEmergency || (halVersion.mMajor < 2 && !independentFromGnss)) {
+ if (isUserEmergency || halVersion.mMajor < 2) {
locationRequest.setLocationSettingsIgnored(true);
durationMillis *= EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER;
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 52e9d7c67605..193107996d1e 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -808,7 +808,7 @@ class MediaRouter2ServiceImpl {
return;
}
- // Can be null if the session is system's.
+ // Can be null if the session is system's or RCN.
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
.findRouterforSessionLocked(uniqueSessionId);
@@ -829,7 +829,7 @@ class MediaRouter2ServiceImpl {
return;
}
- // Can be null if the session is system's.
+ // Can be null if the session is system's or RCN.
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
.findRouterforSessionLocked(uniqueSessionId);
@@ -850,7 +850,7 @@ class MediaRouter2ServiceImpl {
return;
}
- // Can be null if the session is system's.
+ // Can be null if the session is system's or RCN.
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
.findRouterforSessionLocked(uniqueSessionId);
@@ -1232,7 +1232,7 @@ class MediaRouter2ServiceImpl {
route.getOriginalId(), sessionHints);
}
- // routerRecord can be null if the session is system's.
+ // routerRecord can be null if the session is system's or RCN.
private void selectRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord,
@NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route,
@@ -1250,7 +1250,7 @@ class MediaRouter2ServiceImpl {
route.getOriginalId());
}
- // routerRecord can be null if the session is system's.
+ // routerRecord can be null if the session is system's or RCN.
private void deselectRouteOnHandler(long uniqueRequestId,
@Nullable RouterRecord routerRecord,
@NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
@@ -1270,7 +1270,7 @@ class MediaRouter2ServiceImpl {
route.getOriginalId());
}
- // routerRecord can be null if the session is system's.
+ // routerRecord can be null if the session is system's or RCN.
private void transferToRouteOnHandler(long uniqueRequestId,
@Nullable RouterRecord routerRecord,
@NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
@@ -1289,6 +1289,8 @@ class MediaRouter2ServiceImpl {
route.getOriginalId());
}
+ // routerRecord is null if and only if the session is created without the request, which
+ // includes the system's session and RCN cases.
private boolean checkArgumentsForSessionControl(@Nullable RouterRecord routerRecord,
@NonNull String uniqueSessionId, @NonNull MediaRoute2Info route,
@NonNull String description) {
@@ -1305,12 +1307,6 @@ class MediaRouter2ServiceImpl {
return true;
}
- //TODO(b/152950479): Handle RCN case.
- if (routerRecord == null) {
- Slog.w(TAG, "Ignoring " + description + " route from unknown router.");
- return false;
- }
-
RouterRecord matchingRecord = mSessionToRouterMap.get(uniqueSessionId);
if (matchingRecord != routerRecord) {
Slog.w(TAG, "Ignoring " + description + " route from non-matching router. "
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
new file mode 100644
index 000000000000..0bdf3f22ee9a
--- /dev/null
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -0,0 +1,211 @@
+/*
+ * 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.net;
+
+import static android.net.NetworkTemplate.getCollapsedRatType;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.telephony.Annotation;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class that watches for events that are triggered per subscription.
+ */
+// TODO (b/152176562): Write tests to verify subscription changes generate corresponding
+// register/unregister calls.
+public class NetworkStatsSubscriptionsMonitor extends
+ SubscriptionManager.OnSubscriptionsChangedListener {
+
+ /**
+ * Interface that this monitor uses to delegate event handling to NetworkStatsService.
+ */
+ public interface Delegate {
+ /**
+ * Notify that the collapsed RAT type has been changed for any subscription. The method
+ * will also be triggered for any existing sub when start and stop monitoring.
+ *
+ * @param subscriberId IMSI of the subscription.
+ * @param collapsedRatType collapsed RAT type.
+ * @see android.net.NetworkTemplate#getCollapsedRatType(int).
+ */
+ void onCollapsedRatTypeChanged(@NonNull String subscriberId,
+ @Annotation.NetworkType int collapsedRatType);
+ }
+ private final Delegate mDelegate;
+
+ /**
+ * Receivers that watches for {@link ServiceState} changes for each subscription, to
+ * monitor the transitioning between Radio Access Technology(RAT) types for each sub.
+ */
+ @NonNull
+ private final CopyOnWriteArrayList<RatTypeListener> mRatListeners =
+ new CopyOnWriteArrayList<>();
+
+ @NonNull
+ private final SubscriptionManager mSubscriptionManager;
+ @NonNull
+ private final TelephonyManager mTeleManager;
+
+ @NonNull
+ private final Executor mExecutor;
+
+ NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Executor executor,
+ @NonNull Delegate delegate) {
+ super();
+ mSubscriptionManager = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ mExecutor = executor;
+ mDelegate = delegate;
+ }
+
+ @Override
+ public void onSubscriptionsChanged() {
+ // Collect active subId list, hidden subId such as opportunistic subscriptions are
+ // also needed to track CBRS.
+ final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager);
+
+ for (final int subId : newSubs) {
+ final RatTypeListener match = CollectionUtils.find(mRatListeners,
+ it -> it.mSubId == subId);
+ if (match != null) continue;
+
+ // Create listener for every newly added sub. Also store subscriberId into it to
+ // prevent binder call to telephony when querying RAT.
+ final String subscriberId = mTeleManager.getSubscriberId(subId);
+ if (TextUtils.isEmpty(subscriberId)) {
+ Log.wtf(NetworkStatsService.TAG,
+ "Empty subscriberId for newly added sub: " + subId);
+ }
+ final RatTypeListener listener =
+ new RatTypeListener(mExecutor, this, subId, subscriberId);
+ mRatListeners.add(listener);
+
+ // Register listener to the telephony manager that associated with specific sub.
+ mTeleManager.createForSubscriptionId(subId)
+ .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ }
+
+ for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
+ // If the new list contains the subId of the listener, keeps it.
+ final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId);
+ if (match != null) continue;
+
+ handleRemoveRatTypeListener(listener);
+ }
+ }
+
+ @NonNull
+ private List<Integer> getActiveSubIdList(@NonNull SubscriptionManager subscriptionManager) {
+ final ArrayList<Integer> ret = new ArrayList<>();
+ final int[] ids = subscriptionManager.getCompleteActiveSubscriptionIdList();
+ for (int id : ids) ret.add(id);
+ return ret;
+ }
+
+ /**
+ * Get a collapsed RatType for the given subscriberId.
+ *
+ * @param subscriberId the target subscriberId
+ * @return collapsed RatType for the given subscriberId
+ */
+ public int getRatTypeForSubscriberId(@NonNull String subscriberId) {
+ final RatTypeListener match = CollectionUtils.find(mRatListeners,
+ it -> TextUtils.equals(subscriberId, it.mSubscriberId));
+ return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ /**
+ * Start monitoring events that triggered per subscription.
+ */
+ public void start() {
+ mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, this);
+ }
+
+ /**
+ * Unregister subscription changes and all listeners for each subscription.
+ */
+ public void stop() {
+ mSubscriptionManager.removeOnSubscriptionsChangedListener(this);
+
+ for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
+ handleRemoveRatTypeListener(listener);
+ }
+ }
+
+ private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) {
+ mTeleManager.createForSubscriptionId(listener.mSubId)
+ .listen(listener, PhoneStateListener.LISTEN_NONE);
+ mRatListeners.remove(listener);
+
+ // Removal of subscriptions doesn't generate RAT changed event, fire it for every
+ // RatTypeListener.
+ mDelegate.onCollapsedRatTypeChanged(
+ listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ }
+
+ static class RatTypeListener extends PhoneStateListener {
+ // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}.
+ @NonNull
+ private final int mSubId;
+
+ // IMSI to identifying the corresponding network from {@link NetworkState}.
+ // See {@link TelephonyManager#getSubscriberId}.
+ @NonNull
+ private final String mSubscriberId;
+
+ private volatile int mLastCollapsedRatType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ @NonNull
+ private final NetworkStatsSubscriptionsMonitor mMonitor;
+
+ RatTypeListener(@NonNull Executor executor,
+ @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId,
+ @NonNull String subscriberId) {
+ super(executor);
+ mSubId = subId;
+ mSubscriberId = subscriberId;
+ mMonitor = monitor;
+ }
+
+ @Override
+ public void onServiceStateChanged(@NonNull ServiceState ss) {
+ final int networkType = ss.getDataNetworkType();
+ final int collapsedRatType = getCollapsedRatType(networkType);
+ if (collapsedRatType == mLastCollapsedRatType) return;
+
+ if (NetworkStatsService.LOGD) {
+ Log.d(NetworkStatsService.TAG, "subtype changed for sub(" + mSubId + "): "
+ + mLastCollapsedRatType + " -> " + collapsedRatType);
+ }
+ mLastCollapsedRatType = collapsedRatType;
+ mMonitor.mDelegate.onCollapsedRatTypeChanged(mSubscriberId, mLastCollapsedRatType);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index f37af3aef657..9fb468e8db6e 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -724,6 +724,30 @@ public class Installer extends SystemService {
}
/**
+ * Deletes all snapshots of credential encrypted user data, where the snapshot id is not
+ * included in {@code retainSnapshotIds}.
+ *
+ * @param userId id of the user whose user data snapshots to delete.
+ * @param retainSnapshotIds ids of the snapshots that should not be deleted.
+ *
+ * @return {@code true} if the operation was successful, or {@code false} if a remote call
+ * shouldn't be continued. See {@link #checkBeforeRemote}.
+ *
+ * @throws InstallerException if failed to delete user data snapshot.
+ */
+ public boolean destroyCeSnapshotsNotSpecified(@UserIdInt int userId,
+ int[] retainSnapshotIds) throws InstallerException {
+ if (!checkBeforeRemote()) return false;
+
+ try {
+ mInstalld.destroyCeSnapshotsNotSpecified(null, userId, retainSnapshotIds);
+ return true;
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ /**
* Migrates obb data from its legacy location {@code /data/media/obb} to
* {@code /data/media/0/Android/obb}. This call is idempotent and a fast no-op if data has
* already been migrated.
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index a726d39b8595..e3faffa0699b 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -882,7 +882,7 @@ public final class DefaultPermissionGrantPolicy {
public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
Log.i(TAG, "Granting permissions to default browser for user:" + userId);
- grantPermissionsToSystemPackage(packageName, userId, ALWAYS_LOCATION_PERMISSIONS);
+ grantPermissionsToSystemPackage(packageName, userId, FOREGROUND_LOCATION_PERMISSIONS);
}
private String getDefaultSystemHandlerActivityPackage(String intentAction, int userId) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 5f871ad4f9e4..83e99b008b68 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -505,6 +505,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
rollbackIds[i] = mRollbacks.get(i).info.getRollbackId();
}
ApexManager.getInstance().destroyCeSnapshotsNotSpecified(userId, rollbackIds);
+ try {
+ mInstaller.destroyCeSnapshotsNotSpecified(userId, rollbackIds);
+ } catch (Installer.InstallerException ie) {
+ Slog.e(TAG, "Failed to delete snapshots for user: " + userId, ie);
+ }
}
@WorkerThread
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index e5ff8653ec56..78d2afc64f96 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -715,9 +715,13 @@ class ActivityStack extends Task {
private void setWindowingModeInSurfaceTransaction(int preferredWindowingMode,
boolean creating) {
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ if (taskDisplayArea == null) {
+ Slog.d(TAG, "taskDisplayArea is null, bail early");
+ return;
+ }
final int currentMode = getWindowingMode();
final int currentOverrideMode = getRequestedOverrideWindowingMode();
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
final Task topTask = getTopMostTask();
int windowingMode = preferredWindowingMode;
if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index f86aeb2244dc..78ee1de78079 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -76,7 +76,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
import android.annotation.DrawableRes;
@@ -1807,15 +1806,11 @@ public class AppTransition implements Dump {
}
int getAppStackClipMode() {
- // When dismiss keyguard animation occurs, clip before the animation to prevent docked
- // app from showing beyond the divider
- if (mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY
- || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
- return STACK_CLIP_BEFORE_ANIM;
- }
return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
|| mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
|| mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
+ || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY
+ || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
? STACK_CLIP_NONE
: STACK_CLIP_AFTER_ANIM;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a93b962c33b4..2816633045b9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -197,7 +197,6 @@ import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import android.window.ITaskOrganizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -1082,8 +1081,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return null;
}
mShellRoots.put(windowType, root);
- SurfaceControl out = new SurfaceControl();
- out.copyFrom(rootLeash);
+ SurfaceControl out = new SurfaceControl(rootLeash);
return out;
}
@@ -3554,6 +3552,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
onWallpaper, goingToShade, subtle));
}
}, true /* traverseTopToBottom */);
+ for (int i = mShellRoots.size() - 1; i >= 0; --i) {
+ mShellRoots.valueAt(i).startAnimation(policy.createHiddenByKeyguardExit(
+ onWallpaper, goingToShade, subtle));
+ }
}
/** @return {@code true} if there is window to wait before enabling the screen. */
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index 9732637fdd4d..701feff8c6be 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -16,12 +16,20 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
+
import android.annotation.NonNull;
+import android.graphics.Point;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.DisplayInfo;
import android.view.IWindow;
import android.view.SurfaceControl;
+import android.view.animation.Animation;
/**
* Represents a piece of the hierarchy under which a client Shell can manage sub-windows.
@@ -70,5 +78,29 @@ public class ShellRoot {
IWindow getClient() {
return mClient;
}
+
+ void startAnimation(Animation anim) {
+ // Only do this for the divider
+ if (mToken.windowType != TYPE_DOCK_DIVIDER) {
+ return;
+ }
+
+ DisplayInfo displayInfo = mToken.getFixedRotationTransformDisplayInfo();
+ if (displayInfo == null) {
+ displayInfo = mDisplayContent.getDisplayInfo();
+ }
+
+ // Mostly copied from WindowState to enable keyguard transition animation
+ anim.initialize(displayInfo.logicalWidth, displayInfo.logicalHeight,
+ displayInfo.appWidth, displayInfo.appHeight);
+ anim.restrictDuration(MAX_ANIMATION_DURATION);
+ anim.scaleCurrentDuration(mDisplayContent.mWmService.getWindowAnimationScaleLocked());
+ final AnimationAdapter adapter = new LocalAnimationAdapter(
+ new WindowAnimationSpec(anim, new Point(0, 0), false /* canSkipFirstFrame */,
+ 0 /* windowCornerRadius */),
+ mDisplayContent.mWmService.mSurfaceAnimationRunner);
+ mToken.startAnimation(mToken.getPendingTransaction(), adapter, false /* hidden */,
+ ANIMATION_TYPE_WINDOW_ANIMATION, null /* animationFinishedCallback */);
+ }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9adacb8c578d..d31939dec509 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4121,20 +4121,18 @@ class Task extends WindowContainer<WindowContainer> {
* Any time any of these conditions are updated, the updating code should call
* sendTaskAppeared.
*/
- private boolean taskAppearedReady() {
+ boolean taskAppearedReady() {
return mSurfaceControl != null && mTaskOrganizer != null && getHasBeenVisible();
}
private void sendTaskAppeared() {
- if (taskAppearedReady() && !mTaskAppearedSent) {
- mTaskAppearedSent = true;
+ if (mTaskOrganizer != null) {
mAtmService.mTaskOrganizerController.onTaskAppeared(mTaskOrganizer, this);
}
}
private void sendTaskVanished() {
- if (mTaskOrganizer != null && mTaskAppearedSent) {
- mTaskAppearedSent = false;
+ if (mTaskOrganizer != null) {
mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 7fb6f6fcefeb..ec3c99bf0808 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1612,6 +1612,11 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
}
+ @Override
+ boolean canCreateRemoteAnimationTarget() {
+ return true;
+ }
+
/**
* Callback for when the order of the stacks in the display changes.
*/
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 2bbf8dbb274c..22702dd6b566 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -106,19 +106,29 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
void addTask(Task t) {
- mOrganizedTasks.add(t);
- try {
- mOrganizer.onTaskAppeared(t.getTaskInfo());
- } catch (Exception e) {
- Slog.e(TAG, "Exception sending taskAppeared callback" + e);
+ if (t.mTaskAppearedSent) return;
+
+ if (!mOrganizedTasks.contains(t)) {
+ mOrganizedTasks.add(t);
+ }
+ if (t.taskAppearedReady()) {
+ try {
+ t.mTaskAppearedSent = true;
+ mOrganizer.onTaskAppeared(t.getTaskInfo());
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception sending taskAppeared callback" + e);
+ }
}
}
void removeTask(Task t) {
- try {
- mOrganizer.onTaskVanished(t.getTaskInfo());
- } catch (Exception e) {
- Slog.e(TAG, "Exception sending taskVanished callback" + e);
+ if (t.mTaskAppearedSent) {
+ try {
+ t.mTaskAppearedSent = false;
+ mOrganizer.onTaskVanished(t.getTaskInfo());
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception sending taskVanished callback" + e);
+ }
}
mOrganizedTasks.remove(t);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index f6ed2a9a44f4..14e5c6cbf28d 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -379,6 +379,7 @@ class TaskSnapshotSurface implements StartingSurface {
frame = null;
mTmpDstFrame.set(mFrame);
}
+ mTmpDstFrame.offsetTo(0, 0);
// Scale the mismatch dimensions to fill the task bounds
mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 569b8f61c4f4..f6e952c4cea1 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2295,6 +2295,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
mLastLayer = -1;
reassignLayer(t);
+
+ // Leash is now responsible for position, so set our position to 0.
+ t.setPosition(mSurfaceControl, 0, 0);
+ mLastSurfacePosition.set(0, 0);
}
@Override
@@ -2302,6 +2306,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mLastLayer = -1;
mSurfaceFreezer.unfreeze(t);
reassignLayer(t);
+ updateSurfacePosition(t);
}
/**
@@ -2365,11 +2370,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
- void updateSurfacePosition() {
+ final void updateSurfacePosition() {
+ updateSurfacePosition(getPendingTransaction());
+ }
+
+ void updateSurfacePosition(Transaction t) {
// Avoid fighting with the organizer over Surface position.
if (isOrganized()) return;
- if (mSurfaceControl == null) {
+ if (mSurfaceControl == null || mSurfaceAnimator.hasLeash()) {
return;
}
@@ -2378,7 +2387,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return;
}
- getPendingTransaction().setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
+ t.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
}
@@ -2501,9 +2510,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// We need to copy the SurfaceControl instead of returning the original
// because the Parcel FLAGS PARCELABLE_WRITE_RETURN_VALUE cause SurfaceControls
// to release themselves.
- SurfaceControl sc = new SurfaceControl();
- sc.copyFrom(wc.getSurfaceControl());
- return sc;
+ return new SurfaceControl(wc.getSurfaceControl());
}
WindowContainerToken toWindowContainerToken() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d611ad81ca85..5a76bac67d64 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5237,23 +5237,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
super.onAnimationLeashCreated(t, leash);
-
- // Leash is now responsible for position, so set our position to 0.
- t.setPosition(mSurfaceControl, 0, 0);
- mLastSurfacePosition.set(0, 0);
}
@Override
public void onAnimationLeashLost(Transaction t) {
super.onAnimationLeashLost(t);
- updateSurfacePosition(t);
}
@Override
- void updateSurfacePosition() {
- updateSurfacePosition(getPendingTransaction());
- }
-
@VisibleForTesting
void updateSurfacePosition(Transaction t) {
if (mSurfaceControl == null) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 7457a1d05335..23091a00a80b 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -535,8 +535,8 @@ class WindowToken extends WindowContainer<WindowState> {
}
@Override
- void updateSurfacePosition() {
- super.updateSurfacePosition();
+ void updateSurfacePosition(SurfaceControl.Transaction t) {
+ super.updateSurfacePosition(t);
if (isFixedRotationTransforming()) {
// The window is layouted in a simulated rotated display but the real display hasn't
// rotated, so here transforms its surface to fit in the real display.
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 97de1800cae2..2dbbc5ac6806 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -155,11 +155,6 @@ binder::Status BinderIncrementalService::deleteStorage(int32_t storageId) {
return ok();
}
-binder::Status BinderIncrementalService::setStorageParams(int32_t storage, bool enableReadLogs, int32_t* _aidl_return) {
- *_aidl_return = mImpl.setStorageParams(storage, enableReadLogs);
- return ok();
-}
-
binder::Status BinderIncrementalService::makeDirectory(int32_t storageId, const std::string& path,
int32_t* _aidl_return) {
*_aidl_return = mImpl.makeDir(storageId, path);
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index d0357d924586..28613e101b7c 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -71,7 +71,6 @@ public:
binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath,
const std::string& libDirRelativePath,
const std::string& abi, bool* _aidl_return) final;
- binder::Status setStorageParams(int32_t storage, bool enableReadLogs, int32_t* _aidl_return) final;
private:
android::incremental::IncrementalService mImpl;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index de24bcf02a7e..d1153e6cf6e7 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -581,6 +581,7 @@ StorageId IncrementalService::findStorageId(std::string_view path) const {
int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLogs) {
const auto ifs = getIfs(storageId);
if (!ifs) {
+ LOG(ERROR) << "setStorageParams failed, invalid storageId: " << storageId;
return -EINVAL;
}
@@ -1153,6 +1154,7 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
fsControlParcel.incremental->pendingReads.reset(
base::unique_fd(::dup(ifs.control.pendingReads())));
fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs())));
+ fsControlParcel.service = new IncrementalServiceConnector(*this, ifs.mountId);
sp<IncrementalDataLoaderListener> listener =
new IncrementalDataLoaderListener(*this,
externalListener ? *externalListener
@@ -1451,4 +1453,10 @@ void IncrementalService::AppOpsListener::opChanged(int32_t, const String16&) {
incrementalService.onAppOpChanged(packageName);
}
+binder::Status IncrementalService::IncrementalServiceConnector::setStorageParams(
+ bool enableReadLogs, int32_t* _aidl_return) {
+ *_aidl_return = incrementalService.setStorageParams(storage, enableReadLogs);
+ return binder::Status::ok();
+}
+
} // namespace android::incremental
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 9b156464f480..db14a794457e 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -39,6 +39,7 @@
#include "ServiceWrappers.h"
#include "android/content/pm/BnDataLoaderStatusListener.h"
+#include "android/os/incremental/BnIncrementalServiceConnector.h"
#include "incfs.h"
#include "path.h"
@@ -139,7 +140,7 @@ public:
DataLoaderStatusListener externalListener)
: incrementalService(incrementalService), externalListener(externalListener) {}
// Callbacks interface
- binder::Status onStatusChanged(MountId mount, int newStatus) override;
+ binder::Status onStatusChanged(MountId mount, int newStatus) final;
private:
IncrementalService& incrementalService;
@@ -149,13 +150,24 @@ public:
class AppOpsListener : public android::BnAppOpsCallback {
public:
AppOpsListener(IncrementalService& incrementalService, std::string packageName) : incrementalService(incrementalService), packageName(std::move(packageName)) {}
- void opChanged(int32_t op, const String16& packageName) override;
+ void opChanged(int32_t op, const String16& packageName) final;
private:
IncrementalService& incrementalService;
const std::string packageName;
};
+ class IncrementalServiceConnector : public BnIncrementalServiceConnector {
+ public:
+ IncrementalServiceConnector(IncrementalService& incrementalService, int32_t storage)
+ : incrementalService(incrementalService), storage(storage) {}
+ binder::Status setStorageParams(bool enableReadLogs, int32_t* _aidl_return) final;
+
+ private:
+ IncrementalService& incrementalService;
+ int32_t const storage;
+ };
+
private:
static const bool sEnablePerfLogging;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 2ce9c2b9ced0..06ca6c110613 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -192,6 +192,21 @@ public class TaskOrganizerTests extends WindowTestsBase {
}
@Test
+ public void testTaskNoDraw() throws RemoteException {
+ final ActivityStack stack = createStack();
+ final Task task = createTask(stack, false /* fakeDraw */);
+ final ITaskOrganizer organizer = registerMockOrganizer();
+
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ verify(organizer, never()).onTaskAppeared(any());
+ assertTrue(stack.isOrganized());
+
+ mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
+ verify(organizer, never()).onTaskVanished(any());
+ assertFalse(stack.isOrganized());
+ }
+
+ @Test
public void testClearOrganizer() throws RemoteException {
final ActivityStack stack = createStack();
final Task task = createTask(stack);
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index f3e9de0d2688..3048ad7c1fb0 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -311,7 +311,7 @@ public final class LocationAccessPolicy {
}
// If the user or profile is current, permission is granted.
// Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
- return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, uid, pid);
+ return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, pid, uid);
}
private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 327e8b344eeb..1a4b9d735977 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1147,6 +1147,21 @@ public class CarrierConfigManager {
"support_ims_conference_event_package_bool";
/**
+ * Determines whether processing of conference event package data received on a device other
+ * than the conference host is supported.
+ * <p>
+ * When a device A merges calls B and C into a conference it is considered the conference host
+ * and B and C are considered the conference peers.
+ * <p>
+ * When {@code true}, the conference peer will display the conference state if it receives
+ * conference event package data from the network. When {@code false}, the conference peer will
+ * ignore conference event package data received from the network.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL =
+ "support_ims_conference_event_package_on_peer_bool";
+
+ /**
* Determines whether High Definition audio property is displayed in the dialer UI.
* If {@code false}, remove the HD audio property from the connection so that HD audio related
* UI is not displayed. If {@code true}, keep HD audio property as it is configured.
@@ -3780,6 +3795,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false);
sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5);
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 2b1d9e58c4d5..18e25921555a 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -113,6 +113,7 @@ public class DctConstants {
public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 55;
+ public static final int EVENT_SIM_STATE_UPDATED = BASE + 56;
/***** Constants *****/
diff --git a/tests/net/common/java/android/net/DependenciesTest.java b/tests/net/common/java/android/net/DependenciesTest.java
new file mode 100644
index 000000000000..ac1c28a45462
--- /dev/null
+++ b/tests/net/common/java/android/net/DependenciesTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A simple class that tests dependencies to java standard tools from the
+ * Network stack. These tests are not meant to be comprehensive tests of
+ * the relevant APIs : such tests belong in the relevant test suite for
+ * these dependencies. Instead, this just makes sure coverage is present
+ * by calling the methods in the exact way (or a representative way of how)
+ * they are called in the network stack.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DependenciesTest {
+ // Used to in ipmemorystore's RegularMaintenanceJobService to convert
+ // 24 hours into seconds
+ @Test
+ public void testTimeUnit() {
+ final int hours = 24;
+ final long inSeconds = TimeUnit.HOURS.toMillis(hours);
+ assertEquals(inSeconds, hours * 60 * 60 * 1000);
+ }
+
+ private byte[] makeTrivialArray(final int size) {
+ final byte[] src = new byte[size];
+ for (int i = 0; i < size; ++i) {
+ src[i] = (byte) i;
+ }
+ return src;
+ }
+
+ // Used in ApfFilter to find an IP address from a byte array
+ @Test
+ public void testArrays() {
+ final int size = 128;
+ final byte[] src = makeTrivialArray(size);
+
+ // Test copy
+ final int copySize = 16;
+ final int offset = 24;
+ final byte[] expected = new byte[copySize];
+ for (int i = 0; i < copySize; ++i) {
+ expected[i] = (byte) (offset + i);
+ }
+
+ final byte[] copy = Arrays.copyOfRange(src, offset, offset + copySize);
+ assertArrayEquals(expected, copy);
+ assertArrayEquals(new byte[0], Arrays.copyOfRange(src, size, size));
+ }
+
+ // Used mainly in the Dhcp code
+ @Test
+ public void testCopyOf() {
+ final byte[] src = makeTrivialArray(128);
+ final byte[] copy = Arrays.copyOf(src, src.length);
+ assertArrayEquals(src, copy);
+ assertFalse(src == copy);
+
+ assertArrayEquals(new byte[0], Arrays.copyOf(src, 0));
+
+ final int excess = 16;
+ final byte[] biggerCopy = Arrays.copyOf(src, src.length + excess);
+ for (int i = src.length; i < src.length + excess; ++i) {
+ assertEquals(0, biggerCopy[i]);
+ }
+ for (int i = src.length - 1; i >= 0; --i) {
+ assertEquals(src[i], biggerCopy[i]);
+ }
+ }
+
+ // Used mainly in DnsUtils but also various other places
+ @Test
+ public void testAsList() {
+ final int size = 24;
+ final Object[] src = new Object[size];
+ final ArrayList<Object> expected = new ArrayList<>(size);
+ for (int i = 0; i < size; ++i) {
+ final Object o = new Object();
+ src[i] = o;
+ expected.add(o);
+ }
+ assertEquals(expected, Arrays.asList(src));
+ }
+}
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 6e9dc8eaf2dc..3f8261d5ad7f 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -17,6 +17,8 @@
package android.net;
import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+import static android.net.NetworkCapabilities.MAX_TRANSPORT;
+import static android.net.NetworkCapabilities.MIN_TRANSPORT;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
@@ -32,10 +34,12 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVIT
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
+import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
@@ -45,10 +49,15 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.net.wifi.aware.DiscoverySession;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.WifiAwareNetworkSpecifier;
import android.os.Build;
+import android.os.Process;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
@@ -61,6 +70,7 @@ import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import java.util.Arrays;
import java.util.Set;
@@ -74,6 +84,9 @@ public class NetworkCapabilitiesTest {
@Rule
public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
+ private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class);
+ private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
+
private boolean isAtLeastR() {
// BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R.
// Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after
@@ -685,4 +698,238 @@ public class NetworkCapabilitiesTest {
assertEquals(TRANSPORT_VPN, transportTypes[2]);
assertEquals(TRANSPORT_TEST, transportTypes[3]);
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testTelephonyNetworkSpecifier() {
+ final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
+ final NetworkCapabilities nc1 = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier)
+ .build();
+ assertEquals(specifier, nc1.getNetworkSpecifier());
+ try {
+ final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+ .setNetworkSpecifier(specifier)
+ .build();
+ fail("Must have a single transport type. Without transport type or multiple transport"
+ + " types is invalid.");
+ } catch (IllegalStateException expected) { }
+ }
+
+ @Test
+ public void testWifiAwareNetworkSpecifier() {
+ final NetworkCapabilities nc = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI_AWARE);
+ // If NetworkSpecifier is not set, the default value is null.
+ assertNull(nc.getNetworkSpecifier());
+ final WifiAwareNetworkSpecifier specifier = new WifiAwareNetworkSpecifier.Builder(
+ mDiscoverySession, mPeerHandle).build();
+ nc.setNetworkSpecifier(specifier);
+ assertEquals(specifier, nc.getNetworkSpecifier());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testAdministratorUidsAndOwnerUid() {
+ // Test default owner uid.
+ // If the owner uid is not set, the default value should be Process.INVALID_UID.
+ final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build();
+ assertEquals(Process.INVALID_UID, nc1.getOwnerUid());
+ // Test setAdministratorUids and getAdministratorUids.
+ final int[] administratorUids = {1001, 10001};
+ final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+ .setAdministratorUids(administratorUids)
+ .build();
+ assertTrue(Arrays.equals(administratorUids, nc2.getAdministratorUids()));
+ // Test setOwnerUid and getOwnerUid.
+ // The owner UID must be included in administrator UIDs, or throw IllegalStateException.
+ try {
+ final NetworkCapabilities nc3 = new NetworkCapabilities.Builder()
+ .setOwnerUid(1001)
+ .build();
+ fail("The owner UID must be included in administrator UIDs.");
+ } catch (IllegalStateException expected) { }
+ final NetworkCapabilities nc4 = new NetworkCapabilities.Builder()
+ .setAdministratorUids(administratorUids)
+ .setOwnerUid(1001)
+ .build();
+ assertEquals(1001, nc4.getOwnerUid());
+ try {
+ final NetworkCapabilities nc5 = new NetworkCapabilities.Builder()
+ .setAdministratorUids(null)
+ .build();
+ fail("Should not set null into setAdministratorUids");
+ } catch (NullPointerException expected) { }
+ }
+
+ @Test
+ public void testLinkBandwidthKbps() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ // The default value of LinkDown/UpstreamBandwidthKbps should be LINK_BANDWIDTH_UNSPECIFIED.
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkDownstreamBandwidthKbps());
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkUpstreamBandwidthKbps());
+ nc.setLinkDownstreamBandwidthKbps(512);
+ nc.setLinkUpstreamBandwidthKbps(128);
+ assertEquals(512, nc.getLinkDownstreamBandwidthKbps());
+ assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps());
+ assertEquals(128, nc.getLinkUpstreamBandwidthKbps());
+ assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
+ }
+
+ @Test
+ public void testSignalStrength() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ // The default value of signal strength should be SIGNAL_STRENGTH_UNSPECIFIED.
+ assertEquals(SIGNAL_STRENGTH_UNSPECIFIED, nc.getSignalStrength());
+ nc.setSignalStrength(-80);
+ assertEquals(-80, nc.getSignalStrength());
+ assertNotEquals(-50, nc.getSignalStrength());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testDeduceRestrictedCapability() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ // Default capabilities don't have restricted capability.
+ assertFalse(nc.deduceRestrictedCapability());
+ // If there is a force restricted capability, then the network capabilities is restricted.
+ nc.addCapability(NET_CAPABILITY_OEM_PAID);
+ nc.addCapability(NET_CAPABILITY_INTERNET);
+ assertTrue(nc.deduceRestrictedCapability());
+ // Except for the force restricted capability, if there is any unrestricted capability in
+ // capabilities, then the network capabilities is not restricted.
+ nc.removeCapability(NET_CAPABILITY_OEM_PAID);
+ nc.addCapability(NET_CAPABILITY_CBS);
+ assertFalse(nc.deduceRestrictedCapability());
+ // Except for the force restricted capability, the network capabilities will only be treated
+ // as restricted when there is no any unrestricted capability.
+ nc.removeCapability(NET_CAPABILITY_INTERNET);
+ assertTrue(nc.deduceRestrictedCapability());
+ }
+
+ private void assertNoTransport(NetworkCapabilities nc) {
+ for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+ assertFalse(nc.hasTransport(i));
+ }
+ }
+
+ // Checks that all transport types from MIN_TRANSPORT to maxTransportType are set and all
+ // transport types from maxTransportType + 1 to MAX_TRANSPORT are not set when positiveSequence
+ // is true. If positiveSequence is false, then the check sequence is opposite.
+ private void checkCurrentTransportTypes(NetworkCapabilities nc, int maxTransportType,
+ boolean positiveSequence) {
+ for (int i = MIN_TRANSPORT; i <= maxTransportType; i++) {
+ if (positiveSequence) {
+ assertTrue(nc.hasTransport(i));
+ } else {
+ assertFalse(nc.hasTransport(i));
+ }
+ }
+ for (int i = MAX_TRANSPORT; i > maxTransportType; i--) {
+ if (positiveSequence) {
+ assertFalse(nc.hasTransport(i));
+ } else {
+ assertTrue(nc.hasTransport(i));
+ }
+ }
+ }
+
+ @Test
+ public void testMultipleTransportTypes() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ assertNoTransport(nc);
+ // Test adding multiple transport types.
+ for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+ nc.addTransportType(i);
+ checkCurrentTransportTypes(nc, i, true /* positiveSequence */);
+ }
+ // Test removing multiple transport types.
+ for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+ nc.removeTransportType(i);
+ checkCurrentTransportTypes(nc, i, false /* positiveSequence */);
+ }
+ assertNoTransport(nc);
+ nc.addTransportType(TRANSPORT_WIFI);
+ assertTrue(nc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(nc.hasTransport(TRANSPORT_VPN));
+ nc.addTransportType(TRANSPORT_VPN);
+ assertTrue(nc.hasTransport(TRANSPORT_WIFI));
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ nc.removeTransportType(TRANSPORT_WIFI);
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ nc.removeTransportType(TRANSPORT_VPN);
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(nc.hasTransport(TRANSPORT_VPN));
+ assertNoTransport(nc);
+ }
+
+ @Test
+ public void testAddAndRemoveTransportType() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ try {
+ nc.addTransportType(-1);
+ fail("Should not set invalid transport type into addTransportType");
+ } catch (IllegalArgumentException expected) { }
+ try {
+ nc.removeTransportType(-1);
+ fail("Should not set invalid transport type into removeTransportType");
+ } catch (IllegalArgumentException e) { }
+ }
+
+ private class TestTransportInfo implements TransportInfo {
+ TestTransportInfo() {
+ }
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testBuilder() {
+ final int ownerUid = 1001;
+ final int signalStrength = -80;
+ final int requestUid = 10100;
+ final int[] administratorUids = {ownerUid, 10001};
+ final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
+ final TestTransportInfo transportInfo = new TestTransportInfo();
+ final String ssid = "TEST_SSID";
+ final String packageName = "com.google.test.networkcapabilities";
+ final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .removeTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_EIMS)
+ .addCapability(NET_CAPABILITY_CBS)
+ .removeCapability(NET_CAPABILITY_CBS)
+ .setAdministratorUids(administratorUids)
+ .setOwnerUid(ownerUid)
+ .setLinkDownstreamBandwidthKbps(512)
+ .setLinkUpstreamBandwidthKbps(128)
+ .setNetworkSpecifier(specifier)
+ .setTransportInfo(transportInfo)
+ .setSignalStrength(signalStrength)
+ .setSsid(ssid)
+ .setRequestorUid(requestUid)
+ .setRequestorPackageName(packageName)
+ .build();
+ assertEquals(1, nc.getTransportTypes().length);
+ assertEquals(TRANSPORT_WIFI, nc.getTransportTypes()[0]);
+ assertTrue(nc.hasCapability(NET_CAPABILITY_EIMS));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_CBS));
+ assertTrue(Arrays.equals(administratorUids, nc.getAdministratorUids()));
+ assertEquals(ownerUid, nc.getOwnerUid());
+ assertEquals(512, nc.getLinkDownstreamBandwidthKbps());
+ assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps());
+ assertEquals(128, nc.getLinkUpstreamBandwidthKbps());
+ assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
+ assertEquals(specifier, nc.getNetworkSpecifier());
+ assertEquals(transportInfo, nc.getTransportInfo());
+ assertEquals(signalStrength, nc.getSignalStrength());
+ assertNotEquals(-50, nc.getSignalStrength());
+ assertEquals(ssid, nc.getSsid());
+ assertEquals(requestUid, nc.getRequestorUid());
+ assertEquals(packageName, nc.getRequestorPackageName());
+ // Cannot assign null into NetworkCapabilities.Builder
+ try {
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(null);
+ fail("Should not set null into NetworkCapabilities.Builder");
+ } catch (NullPointerException expected) { }
+ assertEquals(nc, new NetworkCapabilities.Builder(nc).build());
+ }
}
diff --git a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
index 9119d62fb023..7b22e45db90a 100644
--- a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
+++ b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
@@ -31,7 +31,6 @@ import android.net.NetworkStats.TAG_NONE
import android.os.Build
import androidx.test.filters.SmallTest
import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.assertFieldCountEquals
import com.android.testutils.assertNetworkStatsEquals
import com.android.testutils.assertParcelingIsLossless
@@ -47,70 +46,22 @@ import kotlin.test.assertEquals
class NetworkStatsApiTest {
@Rule
@JvmField
- val ignoreRule = DevSdkIgnoreRule()
+ val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q)
private val testStatsEmpty = NetworkStats(0L, 0)
+ // Note that these variables need to be initialized outside of constructor, initialize
+ // here with methods that don't exist in Q devices will result in crash.
+
// stats1 and stats2 will have some entries with common keys, which are expected to
// be merged if performing add on these 2 stats.
- private val testStats1 = NetworkStats(0L, 0)
- // Entries which only appear in set1.
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 37, 52, 1, 10, 4))
- // Entries which are common for set1 and set2.
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 17, 2, 11, 1, 0))
- .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 40, 1, 0, 0, 8))
- .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 1, 6, 2, 0))
-
- private val testStats2 = NetworkStats(0L, 0)
- // Entries which are common for set1 and set2.
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1))
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
- .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7))
- .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0))
- // Entry which only appears in set2.
- .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+ private lateinit var testStats1: NetworkStats
+ private lateinit var testStats2: NetworkStats
// This is a result of adding stats1 and stats2, while the merging of common key items is
// subject to test later, this should not be initialized with for a loop to add stats1
// and stats2 above.
- private val testStats3 = NetworkStats(0L, 9)
- // Entries which are unique either in stats1 or stats2.
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
- .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
- // Entries which are common for stats1 and stats2 are being merged.
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 20, 17, 13, 32, 1))
- .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 50, 113, 11, 11, 49))
- .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 51, 3, 3, 4, 15))
- .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 7, 4, 8, 3, 0))
+ private lateinit var testStats3: NetworkStats
companion object {
private const val TEST_IFACE = "test0"
@@ -120,13 +71,67 @@ class NetworkStatsApiTest {
@Before
fun setUp() {
+ testStats1 = NetworkStats(0L, 0)
+ // Entries which only appear in set1.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 37, 52, 1, 10, 4))
+ // Entries which are common for set1 and set2.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 17, 2, 11, 1, 0))
+ .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 40, 1, 0, 0, 8))
+ .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 1, 6, 2, 0))
assertEquals(8, testStats1.size())
+
+ testStats2 = NetworkStats(0L, 0)
+ // Entries which are common for set1 and set2.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+ .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7))
+ .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0))
+ // Entry which only appears in set2.
+ .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
assertEquals(5, testStats2.size())
+
+ testStats3 = NetworkStats(0L, 9)
+ // Entries which are unique either in stats1 or stats2.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+ .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+ // Entries which are common for stats1 and stats2 are being merged.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 20, 17, 13, 32, 1))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 50, 113, 11, 11, 49))
+ .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 51, 3, 3, 4, 15))
+ .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 7, 4, 8, 3, 0))
assertEquals(9, testStats3.size())
}
@Test
- @IgnoreUpTo(Build.VERSION_CODES.Q)
fun testAddEntry() {
val expectedEntriesInStats2 = arrayOf(
Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
@@ -156,7 +161,6 @@ class NetworkStatsApiTest {
}
@Test
- @IgnoreUpTo(Build.VERSION_CODES.Q)
fun testAdd() {
var stats = NetworkStats(0L, 0)
assertNetworkStatsEquals(testStatsEmpty, stats)
@@ -168,7 +172,6 @@ class NetworkStatsApiTest {
}
@Test
- @IgnoreUpTo(Build.VERSION_CODES.Q)
fun testParcelUnparcel() {
assertParcelingIsLossless(testStatsEmpty)
assertParcelingIsLossless(testStats1)
@@ -177,7 +180,6 @@ class NetworkStatsApiTest {
}
@Test
- @IgnoreUpTo(Build.VERSION_CODES.Q)
fun testDescribeContents() {
assertEquals(0, testStatsEmpty.describeContents())
assertEquals(0, testStats1.describeContents())
@@ -186,7 +188,6 @@ class NetworkStatsApiTest {
}
@Test
- @IgnoreUpTo(Build.VERSION_CODES.Q)
fun testSubtract() {
// STATS3 - STATS2 = STATS1
assertNetworkStatsEquals(testStats1, testStats3.subtract(testStats2))
@@ -195,7 +196,6 @@ class NetworkStatsApiTest {
}
@Test
- @IgnoreUpTo(Build.VERSION_CODES.Q)
fun testMethodsDontModifyReceiver() {
listOf(testStatsEmpty, testStats1, testStats2, testStats3).forEach {
val origStats = it.clone()
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index d56f2be7ecb3..b513463ec98f 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -160,7 +160,7 @@ struct AtomDecl {
int exclusiveField = 0;
int defaultState = INT_MAX;
int triggerStateReset = INT_MAX;
- bool nested;
+ bool nested = true;
int uidField = 0;