summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/removed.txt4
-rw-r--r--core/java/android/app/ActivityManager.java9
-rw-r--r--core/java/android/app/ApplicationLoaders.java28
-rw-r--r--core/java/android/app/LoadedApk.java23
-rw-r--r--core/java/android/content/pm/PermissionInfo.java3
-rw-r--r--core/java/android/content/pm/SharedLibraryInfo.java25
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackage.java6
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageImpl.java44
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageRead.java13
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java35
-rw-r--r--core/java/android/metrics/LogMaker.java2
-rw-r--r--core/java/android/text/Html.java2
-rw-r--r--core/java/android/text/format/OWNERS3
-rw-r--r--core/java/android/text/format/Time.java2
-rw-r--r--core/java/android/view/FocusFinder.java19
-rw-r--r--core/java/android/view/autofill/AutofillManager.java65
-rw-r--r--core/java/android/webkit/WebView.java2
-rw-r--r--core/java/android/webkit/WebViewClient.java10
-rw-r--r--core/java/android/widget/Magnifier.java2
-rw-r--r--core/java/com/android/internal/listeners/ListenerExecutor.java16
-rw-r--r--core/java/com/android/internal/os/ClassLoaderFactory.java15
-rw-r--r--core/java/com/android/server/SystemConfig.java82
-rw-r--r--core/jni/com_android_internal_os_ClassLoaderFactory.cpp9
-rw-r--r--core/res/res/values/attrs_manifest.xml23
-rw-r--r--core/tests/coretests/src/android/graphics/PathTest.java4
-rw-r--r--data/etc/platform.xml6
-rw-r--r--data/keyboards/Vendor_2e95_Product_7725.kl64
-rw-r--r--graphics/java/android/graphics/Region.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java19
-rw-r--r--libs/hwui/hwui/Bitmap.cpp7
-rw-r--r--location/java/android/location/GpsStatus.java10
-rw-r--r--location/java/android/location/ILocationListener.aidl2
-rw-r--r--location/java/android/location/ILocationManager.aidl2
-rw-r--r--location/java/android/location/LocationListener.java15
-rw-r--r--location/java/android/location/LocationManager.java51
-rw-r--r--media/java/android/media/ExifInterface.java4
-rw-r--r--media/java/android/media/browse/MediaBrowser.java13
-rw-r--r--media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl4
-rw-r--r--media/java/android/service/media/MediaBrowserService.java2
-rw-r--r--non-updatable-api/removed.txt4
-rw-r--r--packages/CarSystemUI/res/layout/car_user_switching_dialog.xml1
-rw-r--r--packages/CarSystemUI/res/layout/sysui_overlay_window.xml4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java12
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java19
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java48
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java8
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java20
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java256
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java8
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java11
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java4
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml8
-rw-r--r--packages/SystemUI/res/layout/bubbles_manage_button_education.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java148
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java106
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java179
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt163
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java186
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java6
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java28
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java3
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java1
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java2
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java88
-rw-r--r--services/core/java/com/android/server/location/PassiveProvider.java12
-rw-r--r--services/core/java/com/android/server/location/geofence/GeofenceManager.java84
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java73
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java4
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssManagerService.java12
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java8
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java8
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssStatusProvider.java11
-rw-r--r--services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java80
-rw-r--r--services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java2
-rw-r--r--services/core/java/com/android/server/location/util/AppOpsHelper.java84
-rw-r--r--services/core/java/com/android/server/location/util/Injector.java17
-rw-r--r--services/core/java/com/android/server/location/util/LocationAttributionHelper.java24
-rw-r--r--services/core/java/com/android/server/location/util/LocationPermissionsHelper.java107
-rw-r--r--services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java71
-rw-r--r--services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java67
-rw-r--r--services/core/java/com/android/server/location/util/SystemAppOpsHelper.java10
-rw-r--r--services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java65
-rw-r--r--services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java87
-rw-r--r--services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java85
-rw-r--r--services/core/java/com/android/server/media/OWNERS1
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java195
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java52
-rw-r--r--services/core/java/com/android/server/pm/Settings.java17
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java13
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java9
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java21
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java61
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java56
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java49
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java42
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java56
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java73
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java170
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java19
-rw-r--r--tools/aapt2/cmd/Compile.cpp20
-rw-r--r--tools/aapt2/cmd/Compile.h4
-rw-r--r--tools/aapt2/cmd/Compile_test.cpp87
-rw-r--r--tools/aapt2/dump/DumpManifest.cpp25
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp1
134 files changed, 3182 insertions, 1468 deletions
diff --git a/api/removed.txt b/api/removed.txt
index 5a24f625d146..58dbeb8a7d3f 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,10 +1,6 @@
// Signature format: 2.0
package android.app {
- public class ActivityManager {
- method @Deprecated public static int getMaxNumPictureInPictureActions();
- }
-
public class Notification implements android.os.Parcelable {
method @Deprecated public String getChannel();
method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7a6f5584401d..a88c6a890844 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1019,12 +1019,6 @@ public class ActivityManager {
return ActivityTaskManager.getMaxRecentTasksStatic();
}
- /** @removed */
- @Deprecated
- public static int getMaxNumPictureInPictureActions() {
- return 3;
- }
-
/**
* Information you can set and retrieve about the current activity within the recent task list.
*/
@@ -3739,7 +3733,8 @@ public class ActivityManager {
* manner, excessive calls to this API could result a {@link java.lang.RuntimeException}.
* </p>
*
- * @param state The state data
+ * @param state The state data. To be advised, <b>DO NOT</b> include sensitive information/data
+ * (PII, SPII, or other sensitive user data) here. Maximum length is 128 bytes.
*/
public void setProcessStateSummary(@Nullable byte[] state) {
try {
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index bac432e42318..15237beee805 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -48,17 +48,18 @@ public class ApplicationLoaders {
ClassLoader parent, String classLoaderName) {
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
librarySearchPath, libraryPermittedPath, parent, classLoaderName,
- null);
+ null, null);
}
ClassLoader getClassLoaderWithSharedLibraries(
String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String classLoaderName,
- List<ClassLoader> sharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
- libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
+ libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
}
/**
@@ -77,14 +78,22 @@ public class ApplicationLoaders {
return loader;
}
+ // TODO(b/142191088): allow (Java) shared libraries to have <uses-native-library>
+ // Until that is supported, assume that all native shared libraries are used.
+ // "ALL" is a magic string that libnativeloader uses to unconditionally add all available
+ // native shared libraries to the classloader.
+ List<String> nativeSharedLibraries = new ArrayList<>();
+ nativeSharedLibraries.add("ALL");
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
- librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries);
+ librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
}
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String cacheKey,
- String classLoaderName, List<ClassLoader> sharedLibraries) {
+ String classLoaderName, List<ClassLoader> sharedLibraries,
+ List<String> nativeSharedLibraries) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -113,7 +122,8 @@ public class ApplicationLoaders {
ClassLoader classloader = ClassLoaderFactory.createClassLoader(
zip, librarySearchPath, libraryPermittedPath, parent,
- targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
+ targetSdkVersion, isBundled, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -185,7 +195,8 @@ public class ApplicationLoaders {
// assume cached libraries work with current sdk since they are built-in
ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/,
null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/,
- null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/);
+ null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/,
+ null /* nativeSharedLibraries */);
if (classLoader == null) {
// bad configuration or break in classloading code
@@ -255,7 +266,8 @@ public class ApplicationLoaders {
// The cache key is passed separately to enable the stub WebView to be cached under the
// stub's APK path, when the actual package path is the donor APK.
return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
- cacheKey, null /* classLoaderName */, null /* sharedLibraries */);
+ cacheKey, null /* classLoaderName */, null /* sharedLibraries */,
+ null /* nativeSharedLibraries */);
}
/**
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f9b48e710148..1dc54ddbac4b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -412,6 +412,12 @@ public final class LoadedApk {
return;
}
for (SharedLibraryInfo lib : sharedLibraries) {
+ if (lib.isNative()) {
+ // Native shared lib doesn't contribute to the native lib search path. Its name is
+ // sent to libnativeloader and then the native shared lib is exported from the
+ // default linker namespace.
+ continue;
+ }
List<String> paths = lib.getAllCodePaths();
outSeenPaths.addAll(paths);
for (String path : paths) {
@@ -696,6 +702,12 @@ public final class LoadedApk {
}
List<ClassLoader> loaders = new ArrayList<>();
for (SharedLibraryInfo info : sharedLibraries) {
+ if (info.isNative()) {
+ // Native shared lib doesn't contribute to the native lib search path. Its name is
+ // sent to libnativeloader and then the native shared lib is exported from the
+ // default linker namespace.
+ continue;
+ }
loaders.add(createSharedLibraryLoader(
info, isBundledApp, librarySearchPath, libraryPermittedPath));
}
@@ -898,10 +910,19 @@ public final class LoadedApk {
mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
libraryPermittedPath);
+ List<String> nativeSharedLibraries = new ArrayList<>();
+ if (mApplicationInfo.sharedLibraryInfos != null) {
+ for (SharedLibraryInfo info : mApplicationInfo.sharedLibraryInfos) {
+ if (info.isNative()) {
+ nativeSharedLibraries.add(info.getName());
+ }
+ }
+ }
+
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
- mApplicationInfo.classLoaderName, sharedLibraries);
+ mApplicationInfo.classLoaderName, sharedLibraries, nativeSharedLibraries);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
setThreadPolicy(oldPolicy);
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 5f6befdcbaef..e990fd783498 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -525,6 +525,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
protLevel += "|appPredictor";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0) {
+ protLevel += "|companion";
+ }
if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
protLevel += "|retailDemo";
}
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index da2a3d885fc6..862563706da7 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -79,6 +79,7 @@ public final class SharedLibraryInfo implements Parcelable {
private final long mVersion;
private final @Type int mType;
+ private final boolean mIsNative;
private final VersionedPackage mDeclaringPackage;
private final List<VersionedPackage> mDependentPackages;
private List<SharedLibraryInfo> mDependencies;
@@ -93,13 +94,14 @@ public final class SharedLibraryInfo implements Parcelable {
* @param type The lib type.
* @param declaringPackage The package that declares the library.
* @param dependentPackages The packages that depend on the library.
+ * @param isNative indicate if this shared lib is a native lib or not (i.e. java)
*
* @hide
*/
public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
String name, long version, int type,
VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
- List<SharedLibraryInfo> dependencies) {
+ List<SharedLibraryInfo> dependencies, boolean isNative) {
mPath = path;
mPackageName = packageName;
mCodePaths = codePaths;
@@ -109,6 +111,16 @@ public final class SharedLibraryInfo implements Parcelable {
mDeclaringPackage = declaringPackage;
mDependentPackages = dependentPackages;
mDependencies = dependencies;
+ mIsNative = isNative;
+ }
+
+ /** @hide */
+ public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
+ String name, long version, int type,
+ VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
+ List<SharedLibraryInfo> dependencies) {
+ this(path, packageName, codePaths, name, version, type, declaringPackage, dependentPackages,
+ dependencies, false /* isNative */);
}
private SharedLibraryInfo(Parcel parcel) {
@@ -125,6 +137,7 @@ public final class SharedLibraryInfo implements Parcelable {
mDeclaringPackage = parcel.readParcelable(null);
mDependentPackages = parcel.readArrayList(null);
mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR);
+ mIsNative = parcel.readBoolean();
}
/**
@@ -137,6 +150,15 @@ public final class SharedLibraryInfo implements Parcelable {
}
/**
+ * Tells whether this library is a native shared library or not.
+ *
+ * @hide
+ */
+ public boolean isNative() {
+ return mIsNative;
+ }
+
+ /**
* Gets the library name an app defines in its manifest
* to depend on the library.
*
@@ -320,6 +342,7 @@ public final class SharedLibraryInfo implements Parcelable {
parcel.writeParcelable(mDeclaringPackage, flags);
parcel.writeList(mDependentPackages);
parcel.writeTypedList(mDependencies);
+ parcel.writeBoolean(mIsNative);
}
private static String typeToString(int type) {
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 2ee0ad67b108..872098c8689e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -92,6 +92,10 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage addUsesOptionalLibrary(String libraryName);
+ ParsingPackage addUsesNativeLibrary(String libraryName);
+
+ ParsingPackage addUsesOptionalNativeLibrary(String libraryName);
+
ParsingPackage addUsesStaticLibrary(String libraryName);
ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests);
@@ -219,6 +223,8 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage removeUsesOptionalLibrary(String libraryName);
+ ParsingPackage removeUsesOptionalNativeLibrary(String libraryName);
+
ParsingPackage setAnyDensity(int anyDensity);
ParsingPackage setAppComponentFactory(String appComponentFactory);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index f932bc250e28..0c0dc313087e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -186,6 +186,13 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@NonNull
@DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> usesNativeLibraries = emptyList();
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> usesOptionalNativeLibraries = emptyList();
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
private List<String> usesStaticLibraries = emptyList();
@Nullable
private long[] usesStaticLibrariesVersions;
@@ -669,6 +676,27 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
}
@Override
+ public final ParsingPackageImpl addUsesOptionalNativeLibrary(String libraryName) {
+ this.usesOptionalNativeLibraries = CollectionUtils.add(this.usesOptionalNativeLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+ @Override
+ public final ParsingPackageImpl addUsesNativeLibrary(String libraryName) {
+ this.usesNativeLibraries = CollectionUtils.add(this.usesNativeLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+
+ @Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) {
+ this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries,
+ libraryName);
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl addUsesStaticLibrary(String libraryName) {
this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
TextUtils.safeIntern(libraryName));
@@ -982,6 +1010,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
sForInternedStringList.parcel(this.libraryNames, dest, flags);
sForInternedStringList.parcel(this.usesLibraries, dest, flags);
sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
+ sForInternedStringList.parcel(this.usesNativeLibraries, dest, flags);
+ sForInternedStringList.parcel(this.usesOptionalNativeLibraries, dest, flags);
sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
dest.writeLongArray(this.usesStaticLibrariesVersions);
@@ -1144,6 +1174,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
this.libraryNames = sForInternedStringList.unparcel(in);
this.usesLibraries = sForInternedStringList.unparcel(in);
this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
+ this.usesNativeLibraries = sForInternedStringList.unparcel(in);
+ this.usesOptionalNativeLibraries = sForInternedStringList.unparcel(in);
this.usesStaticLibraries = sForInternedStringList.unparcel(in);
this.usesStaticLibrariesVersions = in.createLongArray();
@@ -1417,6 +1449,18 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@NonNull
@Override
+ public List<String> getUsesNativeLibraries() {
+ return usesNativeLibraries;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getUsesOptionalNativeLibraries() {
+ return usesOptionalNativeLibraries;
+ }
+
+ @NonNull
+ @Override
public List<String> getUsesStaticLibraries() {
return usesStaticLibraries;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 5b53c18b820c..7e0fe7dc41bf 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -230,6 +230,19 @@ public interface ParsingPackageRead extends Parcelable {
@NonNull
List<String> getUsesOptionalLibraries();
+ /** @see R.styleabele#AndroidManifestUsesNativeLibrary */
+ @NonNull
+ List<String> getUsesNativeLibraries();
+
+ /**
+ * Like {@link #getUsesNativeLibraries()}, but marked optional by setting
+ * {@link R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is
+ * expected to handle absence manually.
+ * @see R.styleable#AndroidManifestUsesNativeLibrary
+ */
+ @NonNull
+ List<String> getUsesOptionalNativeLibraries();
+
/**
* TODO(b/135203078): Move static library stuff to an inner data class
* @see R.styleable#AndroidManifestUsesStaticLibrary
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 3688f1bda979..e1f08f3e55a1 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -701,6 +701,8 @@ public class ParsingPackageUtils {
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
return parseUsesLibrary(input, pkg, res, parser);
+ case "uses-native-library":
+ return parseUsesNativeLibrary(input, pkg, res, parser);
case "uses-package":
// Dependencies for app installers; we don't currently try to
// enforce this.
@@ -2017,6 +2019,8 @@ public class ParsingPackageUtils {
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
return parseUsesLibrary(input, pkg, res, parser);
+ case "uses-native-library":
+ return parseUsesNativeLibrary(input, pkg, res, parser);
case "processes":
return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
case "uses-package":
@@ -2178,6 +2182,37 @@ public class ParsingPackageUtils {
}
@NonNull
+ private static ParseResult<ParsingPackage> parseUsesNativeLibrary(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesNativeLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String lname = sa.getNonResourceString(
+ R.styleable.AndroidManifestUsesNativeLibrary_name);
+ boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesNativeLibrary_required,
+ true);
+
+ if (lname != null) {
+ if (req) {
+ // Upgrade to treat as stronger constraint
+ pkg.addUsesNativeLibrary(lname)
+ .removeUsesOptionalNativeLibrary(lname);
+ } else {
+ // Ignore if someone already defined as required
+ if (!ArrayUtils.contains(pkg.getUsesNativeLibraries(), lname)) {
+ pkg.addUsesOptionalNativeLibrary(lname);
+ }
+ }
+ }
+
+ return input.success(pkg);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
throws IOException, XmlPullParserException {
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 5496e17206d9..d8a2082f4eae 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -39,7 +39,7 @@ public class LogMaker {
/**
* Min required eventlog line length.
* See: android/util/cts/EventLogTest.java
- * Size checks enforced here are intended only as sanity checks;
+ * Size limits enforced here are intended only as a precaution;
* your logs may be truncated earlier. Please log responsibly.
*
* @hide
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index e3cb382256ae..ab19fa9a1256 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -551,7 +551,7 @@ public class Html {
out.append(((ImageSpan) style[j]).getSource());
out.append("\">");
- // Don't output the dummy character underlying the image.
+ // Don't output the placeholder character underlying the image.
i = next;
}
if (style[j] instanceof AbsoluteSizeSpan) {
diff --git a/core/java/android/text/format/OWNERS b/core/java/android/text/format/OWNERS
new file mode 100644
index 000000000000..32adc12bb901
--- /dev/null
+++ b/core/java/android/text/format/OWNERS
@@ -0,0 +1,3 @@
+# Inherits OWNERS from parent directory, plus the following
+
+vichang@google.com
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 80c8ec852146..5b5c8548f281 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -361,7 +361,7 @@ public class Time {
*/
@Override
public String toString() {
- // toString() uses its own TimeCalculator rather than the shared one. Otherwise crazy stuff
+ // toString() uses its own TimeCalculator rather than the shared one. Otherwise weird stuff
// happens during debugging when the debugger calls toString().
TimeCalculator calculator = new TimeCalculator(this.timezone);
calculator.copyFieldsFromTime(this);
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 064bc6947fc4..713cfb48c95f 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -311,9 +311,6 @@ public class FocusFinder {
}
final int count = focusables.size();
- if (count < 2) {
- return null;
- }
switch (direction) {
case View.FOCUS_FORWARD:
return getNextFocusable(focused, focusables, count);
@@ -376,29 +373,29 @@ public class FocusFinder {
}
private static View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
- if (count < 2) {
- return null;
- }
if (focused != null) {
int position = focusables.lastIndexOf(focused);
if (position >= 0 && position + 1 < count) {
return focusables.get(position + 1);
}
}
- return focusables.get(0);
+ if (!focusables.isEmpty()) {
+ return focusables.get(0);
+ }
+ return null;
}
private static View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
- if (count < 2) {
- return null;
- }
if (focused != null) {
int position = focusables.indexOf(focused);
if (position > 0) {
return focusables.get(position - 1);
}
}
- return focusables.get(count - 1);
+ if (!focusables.isEmpty()) {
+ return focusables.get(count - 1);
+ }
+ return null;
}
private static View getNextKeyboardNavigationCluster(
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index fbfeda6f0bcc..b398cf6c9cb3 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -727,16 +727,22 @@ public final class AutofillManager {
mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
if (mSessionId != NO_SESSION) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
final AutofillClient client = getClient();
if (client != null) {
final SyncResultReceiver receiver = new SyncResultReceiver(
SYNC_CALLS_TIMEOUT_MS);
try {
- mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
- mServiceClient.asBinder(), receiver);
- final boolean sessionWasRestored = receiver.getIntResult() == 1;
+ boolean sessionWasRestored = false;
+ if (clientAdded) {
+ mService.restoreSession(mSessionId,
+ client.autofillClientGetActivityToken(),
+ mServiceClient.asBinder(), receiver);
+ sessionWasRestored = receiver.getIntResult() == 1;
+ } else {
+ Log.w(TAG, "No service client for session " + mSessionId);
+ }
if (!sessionWasRestored) {
Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -850,8 +856,8 @@ public final class AutofillManager {
if (isDisabledByServiceLocked()) {
return false;
}
- ensureServiceClientAddedIfNeededLocked();
- return mEnabled;
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+ return clientAdded ? mEnabled : false;
}
}
@@ -1007,7 +1013,12 @@ public final class AutofillManager {
AutofillCallback callback = null;
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+
+ if (!clientAdded) {
+ if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
+ return callback;
+ }
if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
@@ -1060,9 +1071,10 @@ public final class AutofillManager {
@GuardedBy("mLock")
void notifyViewExitedLocked(@NonNull View view) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
- if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
+ if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
+ && isActiveLocked()) {
// dont notify exited when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = view.getAutofillId();
@@ -1178,7 +1190,12 @@ public final class AutofillManager {
AutofillCallback callback = null;
if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+
+ if (!clientAdded) {
+ if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
+ return callback;
+ }
if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
if (sVerbose) {
@@ -1241,9 +1258,10 @@ public final class AutofillManager {
@GuardedBy("mLock")
private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
- if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
+ if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
+ && isActiveLocked()) {
// don't notify exited when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view, virtualId);
@@ -1905,11 +1923,16 @@ public final class AutofillManager {
}
}
+ /**
+ * Tries to add AutofillManagerClient to service if it does not been added. Returns {@code true}
+ * if the AutofillManagerClient is added successfully or is already added. Otherwise,
+ * returns {@code false}.
+ */
@GuardedBy("mLock")
- private void ensureServiceClientAddedIfNeededLocked() {
+ private boolean tryAddServiceClientIfNeededLocked() {
final AutofillClient client = getClient();
if (client == null) {
- return;
+ return false;
}
if (mServiceClient == null) {
@@ -1924,7 +1947,10 @@ public final class AutofillManager {
flags = receiver.getIntResult();
} catch (SyncResultReceiver.TimeoutException e) {
Log.w(TAG, "Failed to initialize autofill: " + e);
- return;
+ // Reset the states initialized above.
+ mService.removeClient(mServiceClient, userId);
+ mServiceClient = null;
+ return false;
}
mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
@@ -1949,6 +1975,7 @@ public final class AutofillManager {
throw e.rethrowFromSystemServer();
}
}
+ return true;
}
@GuardedBy("mLock")
@@ -1962,12 +1989,13 @@ public final class AutofillManager {
&& view.isLaidOut()
&& view.isVisibleToUser()) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
if (sVerbose) {
- Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
+ Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled + " mServiceClient="
+ + mServiceClient);
}
- if (mEnabled && !isClientDisablingEnterExitEvent()) {
+ if (clientAdded && mEnabled && !isClientDisablingEnterExitEvent()) {
final AutofillId id = view.getAutofillId();
final AutofillValue value = view.getAutofillValue();
// Starts new session.
@@ -2692,6 +2720,7 @@ public final class AutofillManager {
pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
pw.print(pfx); pw.print("context: "); pw.println(mContext);
+ pw.print(pfx); pw.print("service client: "); pw.println(mServiceClient);
final AutofillClient client = getClient();
if (client != null) {
pw.print(pfx); pw.print("client: "); pw.print(client);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1c75232dc15c..052bca57d77c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1511,7 +1511,7 @@ public class WebView extends AbsoluteLayout
*
* @param hosts the list of hosts
* @param callback will be called with {@code true} if hosts are successfully added to the
- * whitelist. It will be called with {@code false} if any hosts are malformed. The callback
+ * allowlist. It will be called with {@code false} if any hosts are malformed. The callback
* will be run on the UI thread
*/
public static void setSafeBrowsingWhitelist(@NonNull List<String> hosts,
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 150fa88a36e3..7b6e1a370479 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -173,8 +173,9 @@ public class WebViewClient {
* when accessing private data or the view system.
*
* <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
- * Browsing checks. If this is undesired, whitelist the URL with {@link
- * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+ * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+ * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+ * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
@@ -211,8 +212,9 @@ public class WebViewClient {
* when accessing private data or the view system.
*
* <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
- * Browsing checks. If this is undesired, whitelist the URL with {@link
- * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+ * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+ * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+ * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 23bbe69afafb..89206fda39f3 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -1142,7 +1142,7 @@ public final class Magnifier {
bitmapRenderNode.setOutline(outline);
bitmapRenderNode.setClipToOutline(true);
- // Create a dummy draw, which will be replaced later with real drawing.
+ // Create a placeholder draw, which will be replaced later with real drawing.
final RecordingCanvas canvas = bitmapRenderNode.beginRecording(
mContentWidth, mContentHeight);
try {
diff --git a/core/java/com/android/internal/listeners/ListenerExecutor.java b/core/java/com/android/internal/listeners/ListenerExecutor.java
index e78e32b829b3..9979e6056f50 100644
--- a/core/java/com/android/internal/listeners/ListenerExecutor.java
+++ b/core/java/com/android/internal/listeners/ListenerExecutor.java
@@ -40,7 +40,7 @@ public interface ListenerExecutor {
/**
* Called before this operation is to be run. Some operations may be canceled before they
* are run, in which case this method may not be called. {@link #onPostExecute(boolean)}
- * will only be run if this method was run.
+ * will only be run if this method was run. This callback is invoked on the calling thread.
*/
default void onPreExecute() {}
@@ -49,7 +49,7 @@ public interface ListenerExecutor {
* RuntimeException, which will propagate normally. Implementations of
* {@link ListenerExecutor} have the option to override
* {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to
- * intercept failures at the class level.
+ * intercept failures at the class level. This callback is invoked on the executor thread.
*/
default void onFailure(Exception e) {
// implementations should handle any exceptions that may be thrown
@@ -59,21 +59,24 @@ public interface ListenerExecutor {
/**
* Called after the operation is run. This method will always be called if
* {@link #onPreExecute()} is called. Success implies that the operation was run to
- * completion with no failures.
+ * completion with no failures. This callback may be invoked on the calling thread or
+ * executor thread.
*/
default void onPostExecute(boolean success) {}
/**
* Called after this operation is complete (which does not imply that it was necessarily
* run). Will always be called once per operation, no matter if the operation was run or
- * not. Success implies that the operation was run to completion with no failures.
+ * not. Success implies that the operation was run to completion with no failures. This
+ * callback may be invoked on the calling thread or executor thread.
*/
default void onComplete(boolean success) {}
}
/**
* May be override to handle operation failures at a class level. Will not be invoked in the
- * event of a RuntimeException, which will propagate normally.
+ * event of a RuntimeException, which will propagate normally. This callback is invoked on the
+ * executor thread.
*/
default <TListener> void onOperationFailure(ListenerOperation<TListener> operation,
Exception exception) {
@@ -83,7 +86,8 @@ public interface ListenerExecutor {
/**
* Executes the given listener operation on the given executor, using the provided listener
* supplier. If the supplier returns a null value, or a value during the operation that does not
- * match the value prior to the operation, then the operation is considered canceled.
+ * match the value prior to the operation, then the operation is considered canceled. If a null
+ * operation is supplied, nothing happens.
*/
default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
@Nullable ListenerOperation<TListener> operation) {
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index a18943c264f5..f83c5bdc4e28 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -101,7 +101,7 @@ public class ClassLoaderFactory {
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) {
return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath,
- parent, targetSdkVersion, isNamespaceShared, classLoaderName, null);
+ parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null);
}
@@ -111,18 +111,24 @@ public class ClassLoaderFactory {
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
- List<ClassLoader> sharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
classLoaderName, sharedLibraries);
+ String sonameList = "";
+ if (nativeSharedLibraries != null) {
+ sonameList = String.join(":", nativeSharedLibraries);
+ }
+
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
String errorMessage = createClassloaderNamespace(classLoader,
targetSdkVersion,
librarySearchPath,
libraryPermittedPath,
isNamespaceShared,
- dexPath);
+ dexPath,
+ sonameList);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (errorMessage != null) {
@@ -139,5 +145,6 @@ public class ClassLoaderFactory {
String librarySearchPath,
String libraryPermittedPath,
boolean isNamespaceShared,
- String dexPath);
+ String dexPath,
+ String sonameList);
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 0eb3981ed598..c6a1153c747f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -49,10 +49,12 @@ import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -104,11 +106,17 @@ public class SystemConfig {
public final String name;
public final String filename;
public final String[] dependencies;
+ public final boolean isNative;
SharedLibraryEntry(String name, String filename, String[] dependencies) {
+ this(name, filename, dependencies, false /* isNative */);
+ }
+
+ SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) {
this.name = name;
this.filename = filename;
this.dependencies = dependencies;
+ this.isNative = isNative;
}
}
@@ -170,12 +178,6 @@ public class SystemConfig {
// URL-handling state upon factory reset.
final ArraySet<String> mLinkedApps = new ArraySet<>();
- // These are the packages that are whitelisted to be able to run as system user
- final ArraySet<String> mSystemUserWhitelistedApps = new ArraySet<>();
-
- // These are the packages that should not run under system user
- final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
-
// These are the components that are enabled by default as VR mode listener services.
final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>();
@@ -309,14 +311,6 @@ public class SystemConfig {
return mLinkedApps;
}
- public ArraySet<String> getSystemUserWhitelistedApps() {
- return mSystemUserWhitelistedApps;
- }
-
- public ArraySet<String> getSystemUserBlacklistedApps() {
- return mSystemUserBlacklistedApps;
- }
-
public ArraySet<String> getHiddenApiWhitelistedApps() {
return mHiddenApiPackageWhitelist;
}
@@ -457,6 +451,7 @@ public class SystemConfig {
log.traceBegin("readAllPermissions");
try {
readAllPermissions();
+ readPublicNativeLibrariesList();
} finally {
log.traceEnd();
}
@@ -895,34 +890,6 @@ public class SystemConfig {
}
XmlUtils.skipCurrentTag(parser);
} break;
- case "system-user-whitelisted-app": {
- if (allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<" + name + "> without package in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- mSystemUserWhitelistedApps.add(pkgname);
- }
- } else {
- logNotAllowedInPartition(name, permFile, parser);
- }
- XmlUtils.skipCurrentTag(parser);
- } break;
- case "system-user-blacklisted-app": {
- if (allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<" + name + "> without package in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- mSystemUserBlacklistedApps.add(pkgname);
- }
- } else {
- logNotAllowedInPartition(name, permFile, parser);
- }
- XmlUtils.skipCurrentTag(parser);
- } break;
case "default-enabled-vr-app": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
@@ -1513,6 +1480,37 @@ public class SystemConfig {
}
}
+ private void readPublicNativeLibrariesList() {
+ readPublicLibrariesListFile(new File("/vendor/etc/public.libraries.txt"));
+ String[] dirs = {"/system/etc", "/system_ext/etc", "/product/etc"};
+ for (String dir : dirs) {
+ for (File f : (new File(dir)).listFiles()) {
+ String name = f.getName();
+ if (name.startsWith("public.libraries-") && name.endsWith(".txt")) {
+ readPublicLibrariesListFile(f);
+ }
+ }
+ }
+ }
+
+ private void readPublicLibrariesListFile(File listFile) {
+ try (BufferedReader br = new BufferedReader(new FileReader(listFile))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (line.isEmpty() || line.startsWith("#")) {
+ continue;
+ }
+ // Line format is <soname> [abi]. We take the soname part.
+ String soname = line.trim().split(" ")[0];
+ SharedLibraryEntry entry = new SharedLibraryEntry(
+ soname, soname, new String[0], true);
+ mSharedLibraries.put(entry.name, entry);
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to read public libraries file " + listFile, e);
+ }
+ }
+
private static boolean isSystemProcess() {
return Process.myUid() == Process.SYSTEM_UID;
}
diff --git a/core/jni/com_android_internal_os_ClassLoaderFactory.cpp b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
index f8d41e4bef54..59c413ff58a6 100644
--- a/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
+++ b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
@@ -28,16 +28,19 @@ static jstring createClassloaderNamespace_native(JNIEnv* env,
jstring librarySearchPath,
jstring libraryPermittedPath,
jboolean isShared,
- jstring dexPath) {
+ jstring dexPath,
+ jstring sonameList) {
return android::CreateClassLoaderNamespace(env, targetSdkVersion,
classLoader, isShared == JNI_TRUE,
dexPath,
- librarySearchPath, libraryPermittedPath);
+ librarySearchPath,
+ libraryPermittedPath,
+ sonameList);
}
static const JNINativeMethod g_methods[] = {
{ "createClassloaderNamespace",
- "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Ljava/lang/String;",
+ "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
reinterpret_cast<void*>(createClassloaderNamespace_native) },
};
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index eb30c9be4eba..ac08d96ab303 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2173,6 +2173,29 @@
<attr name="required" />
</declare-styleable>
+ <!-- The <code>uses-native-library</code> specifies a native shared library that this
+ package requires to be linked against. Specifying this flag tells the
+ system to make the native library to be available to your app.
+
+ <p>On devices running R or lower, this is ignored and the app has access to all
+ the public native shared libraries that are exported from the platform. This is
+ also ignored if the app is targeting R or lower.
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestUsesNativeLibrary" parent="AndroidManifestApplication">
+ <!-- Required name of the library you use. -->
+ <attr name="name" />
+ <!-- Specify whether this native library is required for the application.
+ The default is true, meaning the application requires the
+ library, and does not want to be installed on devices that
+ don't support it. If you set this to false, then this will
+ allow the application to be installed even if the library
+ doesn't exist, and you will need to check for its presence
+ dynamically at runtime. -->
+ <attr name="required" />
+ </declare-styleable>
+
<!-- The <code>uses-static-library</code> specifies a shared <strong>static</strong>
library that this package requires to be statically linked against. Specifying
this tag tells the system to include this library's code in your class loader.
diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java
index c6d6d1ff90d5..b50792ca6b38 100644
--- a/core/tests/coretests/src/android/graphics/PathTest.java
+++ b/core/tests/coretests/src/android/graphics/PathTest.java
@@ -28,7 +28,9 @@ public class PathTest extends TestCase {
final Path.FillType defaultFillType = path.getFillType();
final Path.FillType fillType = Path.FillType.INVERSE_EVEN_ODD;
- assertFalse(fillType.equals(defaultFillType)); // Sanity check for the test itself.
+
+ // This test is only meaningful if it changes from the default.
+ assertFalse(fillType.equals(defaultFillType));
path.setFillType(fillType);
path.reset();
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index e1f6b2aa76ab..dd8f40d586bc 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -263,10 +263,4 @@
be able to connect to the internet when such a proxy is in use, since
all outgoing connections originate from this app. -->
<allow-in-power-save-except-idle package="com.android.proxyhandler" />
-
- <!-- These are the packages that are white-listed to be able to run as system user -->
- <system-user-whitelisted-app package="com.android.settings" />
-
- <!-- These are the packages that shouldn't run as system user -->
- <system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
</permissions>
diff --git a/data/keyboards/Vendor_2e95_Product_7725.kl b/data/keyboards/Vendor_2e95_Product_7725.kl
new file mode 100644
index 000000000000..7672e22f8adc
--- /dev/null
+++ b/data/keyboards/Vendor_2e95_Product_7725.kl
@@ -0,0 +1,64 @@
+# 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.
+
+#
+# Scuf Vantage Controller
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# L2 Trigger axis
+axis 0x03 LTRIGGER
+# R2 Trigger axis
+axis 0x04 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index d8d96413a93d..43373ffbd3f4 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -409,10 +409,10 @@ public class Region implements Parcelable {
mNativeRegion = ni;
}
- /* add dummy parameter so constructor can be called from jni without
+ /* Add an unused parameter so constructor can be called from jni without
triggering 'not cloneable' exception */
@UnsupportedAppUsage
- private Region(long ni, int dummy) {
+ private Region(long ni, int unused) {
this(ni);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index cb64796196ed..338ece5afbc2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -30,6 +30,7 @@ import android.os.ServiceManager;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -60,20 +61,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
private static final int DIRECTION_HIDE = 2;
private static final int FLOATING_IME_BOTTOM_INSET = -80;
- protected final SystemWindows mSystemWindows;
+ protected final IWindowManager mWmService;
protected final Handler mHandler;
- final TransactionPool mTransactionPool;
-
- final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
-
- final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
-
- public DisplayImeController(SystemWindows syswin, DisplayController displayController,
- Handler handler, TransactionPool transactionPool) {
- mHandler = handler;
- mSystemWindows = syswin;
+ private final TransactionPool mTransactionPool;
+ private final DisplayController mDisplayController;
+ private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
+ private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
+
+ public DisplayImeController(IWindowManager wmService, DisplayController displayController,
+ Handler mainHandler, TransactionPool transactionPool) {
+ mHandler = mainHandler;
+ mWmService = wmService;
mTransactionPool = transactionPool;
- displayController.addDisplayWindowListener(this);
+ mDisplayController = displayController;
+ mDisplayController.addDisplayWindowListener(this);
}
@Override
@@ -81,9 +82,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
// Add's a system-ui window-manager specifically for ime. This type is special because
// WM will defer IME inset handling to it in multi-window scenarious.
PerDisplay pd = new PerDisplay(displayId,
- mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation());
+ mDisplayController.getDisplayLayout(displayId).rotation());
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+ mWmService.setDisplayWindowInsetsController(displayId, pd);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + displayId);
}
@@ -96,7 +97,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
if (pd == null) {
return;
}
- if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
+ if (mDisplayController.getDisplayLayout(displayId).rotation()
!= pd.mRotation && isImeShowing(displayId)) {
pd.startAnimation(true, false /* forceRestart */);
}
@@ -105,7 +106,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
@Override
public void onDisplayRemoved(int displayId) {
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+ mWmService.setDisplayWindowInsetsController(displayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
}
@@ -263,7 +264,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
private void setVisibleDirectly(boolean visible) {
mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
try {
- mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
} catch (RemoteException e) {
}
}
@@ -282,7 +283,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
// an IME inset). For now, we assume that no non-floating IME will be <= this nav bar
// frame height so any reported frame that is <= nav-bar frame height is assumed to
// be floating.
- return frame.height() <= mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId)
+ return frame.height() <= mDisplayController.getDisplayLayout(mDisplayId)
.navBarFrameHeight();
}
@@ -297,8 +298,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
// This is a "floating" or "expanded" IME, so to get animations, just
// pretend the ime has some size just below the screen.
mImeFrame.set(newFrame);
- final int floatingInset = (int) (
- mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId)
+ final int floatingInset = (int) (mDisplayController.getDisplayLayout(mDisplayId)
.density() * FLOATING_IME_BOTTOM_INSET);
mImeFrame.bottom -= floatingInset;
} else if (newFrame.height() != 0) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 34018e8cb305..8abe9eeb6a9a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -60,11 +60,10 @@ public class SystemWindows {
private static final String TAG = "SystemWindows";
private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>();
- final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
- public Context mContext;
- public IWindowManager mWmService;
- IWindowSession mSession;
- DisplayController mDisplayController;
+ private final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
+ private final DisplayController mDisplayController;
+ private final IWindowManager mWmService;
+ private IWindowSession mSession;
private final DisplayController.OnDisplaysChangedListener mDisplayListener =
new DisplayController.OnDisplaysChangedListener() {
@@ -84,9 +83,7 @@ public class SystemWindows {
public void onDisplayRemoved(int displayId) { }
};
- public SystemWindows(Context context, DisplayController displayController,
- IWindowManager wmService) {
- mContext = context;
+ public SystemWindows(DisplayController displayController, IWindowManager wmService) {
mWmService = wmService;
mDisplayController = displayController;
mDisplayController.addDisplayWindowListener(mDisplayListener);
@@ -210,8 +207,8 @@ public class SystemWindows {
}
final Display display = mDisplayController.getDisplay(mDisplayId);
SurfaceControlViewHost viewRoot =
- new SurfaceControlViewHost(mContext, display, wwm,
- true /* useSfChoreographer */);
+ new SurfaceControlViewHost(
+ view.getContext(), display, wwm, true /* useSfChoreographer */);
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
viewRoot.setView(view, attrs);
mViewRoots.put(view, viewRoot);
@@ -313,7 +310,7 @@ public class SystemWindows {
}
}
- class ContainerWindow extends IWindow.Stub {
+ static class ContainerWindow extends IWindow.Stub {
ContainerWindow() {}
@Override
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index c0a24438987a..1a89cfd5d0ad 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -209,11 +209,8 @@ static SkImageInfo validateAlpha(const SkImageInfo& info) {
void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes) {
mInfo = validateAlpha(newInfo);
- // Dirty hack is dirty
- // TODO: Figure something out here, Skia's current design makes this
- // really hard to work with. Skia really, really wants immutable objects,
- // but with the nested-ref-count hackery going on that's just not
- // feasible without going insane trying to figure it out
+ // TODO: Skia intends for SkPixelRef to be immutable, but this method
+ // modifies it. Find another way to support reusing the same pixel memory.
this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes);
}
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 496885cd1f37..997339eb2a80 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -151,6 +151,16 @@ public final class GpsStatus {
return status;
}
+ /**
+ * Builds an empty GpsStatus. Should only be used for legacy reasons.
+ *
+ * @hide
+ */
+ @NonNull
+ static GpsStatus createEmpty() {
+ return new GpsStatus();
+ }
+
private GpsStatus() {
}
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 6e7f6a52d669..29b483af8721 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -24,6 +24,6 @@ import android.os.IRemoteCallback;
*/
oneway interface ILocationListener
{
- void onLocationChanged(in Location location, in IRemoteCallback onCompleteCallback);
+ void onLocationChanged(in Location location, in @nullable IRemoteCallback onCompleteCallback);
void onProviderEnabledChanged(String provider, boolean enabled);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index bb8f81dfaa32..0e7eaa21888e 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -113,7 +113,7 @@ interface ILocationManager
List<LocationRequest> getTestProviderCurrentRequests(String provider);
LocationTime getGnssTimeMillis();
- boolean sendExtraCommand(String provider, String command, inout Bundle extras);
+ void sendExtraCommand(String provider, String command, inout Bundle extras);
// used by gts tests to verify whitelists
String[] getBackgroundThrottlingWhitelist();
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 8df08345c79b..2738ff4ff38c 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -36,7 +36,9 @@ import android.os.Bundle;
public interface LocationListener {
/**
- * Called when the location has changed.
+ * Called when the location has changed. A wakelock is held on behalf on the listener for some
+ * brief amount of time as this callback executes. If this callback performs long running
+ * operations, it is the client's responsibility to obtain their own wakelock.
*
* @param location the updated location
*/
@@ -52,18 +54,17 @@ public interface LocationListener {
default void onStatusChanged(String provider, int status, Bundle extras) {}
/**
- * Called when the provider is enabled by the user.
+ * Called when a provider this listener is registered with becomes enabled.
*
- * @param provider the name of the location provider that has become enabled
+ * @param provider the name of the location provider
*/
default void onProviderEnabled(@NonNull String provider) {}
/**
- * Called when the provider is disabled by the user. If requestLocationUpdates
- * is called on an already disabled provider, this method is called
- * immediately.
+ * Called when the provider this listener is registered with becomes disabled. If a provider is
+ * disabled when this listener is registered, this callback will be invoked immediately.
*
- * @param provider the name of the location provider that has become disabled
+ * @param provider the name of the location provider
*/
default void onProviderDisabled(@NonNull String provider) {}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c0b8e1bf3bbe..1803027743f6 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -303,7 +303,6 @@ public class LocationManager {
public static final String METADATA_SETTINGS_FOOTER_STRING =
"com.android.settings.location.FOOTER_STRING";
-
private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
@GuardedBy("sLocationListeners")
@@ -311,7 +310,9 @@ public class LocationManager {
sLocationListeners = new WeakHashMap<>();
final Context mContext;
- @UnsupportedAppUsage
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+ + "LocationManager}")
final ILocationManager mService;
private final Object mLock = new Object();
@@ -421,8 +422,7 @@ public class LocationManager {
try {
return mService.getExtraLocationControllerPackage();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -437,7 +437,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackage(packageName);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -452,7 +452,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackageEnabled(enabled);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -466,8 +466,7 @@ public class LocationManager {
try {
return mService.isExtraLocationControllerPackageEnabled();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return false;
+ throw e.rethrowFromSystemServer();
}
}
@@ -485,7 +484,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackage(packageName);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -503,7 +502,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackageEnabled(enabled);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -1199,7 +1198,7 @@ public class LocationManager {
mContext.getPackageName(), mContext.getAttributionTag(),
AppOpsManager.toReceiverId(listener));
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -1301,7 +1300,7 @@ public class LocationManager {
// unregistration is complete.
mService.unregisterLocationListener(transport);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -1517,7 +1516,8 @@ public class LocationManager {
Preconditions.checkArgument(command != null, "invalid null command");
try {
- return mService.sendExtraCommand(provider, command, extras);
+ mService.sendExtraCommand(provider, command, extras);
+ return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1835,6 +1835,10 @@ public class LocationManager {
} else {
status.setStatus(gnssStatus, ttff);
}
+ } else if (status == null) {
+ // even though this method is marked as nullable, legacy behavior was to never return
+ // a null result, and there are applications that rely on this behavior.
+ status = GpsStatus.createEmpty();
}
return status;
}
@@ -2424,7 +2428,7 @@ public class LocationManager {
try {
cancellationSignal.cancel();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -2464,7 +2468,8 @@ public class LocationManager {
}
@Override
- public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
+ public void onLocationChanged(Location location,
+ @Nullable IRemoteCallback onCompleteCallback) {
executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
@Override
public void operate(LocationListener listener) {
@@ -2473,7 +2478,13 @@ public class LocationManager {
@Override
public void onComplete(boolean success) {
- markComplete(onCompleteCallback);
+ if (onCompleteCallback != null) {
+ try {
+ onCompleteCallback.sendResult(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
});
}
@@ -2488,14 +2499,6 @@ public class LocationManager {
}
});
}
-
- private void markComplete(IRemoteCallback onCompleteCallback) {
- try {
- onCompleteCallback.sendResult(null);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
}
private static class NmeaAdapter extends GnssStatus.Callback implements OnNmeaMessageListener {
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 6d690f0aa397..4a6724a09c1e 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1411,9 +1411,9 @@ public class ExifInterface {
private static final int IMAGE_TYPE_WEBP = 14;
static {
- sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+ sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.US);
sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
- sFormatterTz = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss XXX");
+ sFormatterTz = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss XXX", Locale.US);
sFormatterTz.setTimeZone(TimeZone.getTimeZone("UTC"));
// Build up the hash tables to look up Exif tags for reading Exif tags.
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 3c2be5f93e30..029e61492b6d 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -25,7 +25,6 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ParceledListSlice;
import android.media.MediaDescription;
-import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.os.Binder;
import android.os.Bundle;
@@ -757,8 +756,8 @@ public final class MediaBrowser {
* Flag: Indicates that the item is playable.
* <p>
* The id of this item may be passed to
- * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
- * to start playing it.
+ * {@link android.media.session.MediaController.TransportControls
+ * #playFromMediaId(String, Bundle)} to start playing it.
* </p>
*/
public static final int FLAG_PLAYABLE = 1 << 1;
@@ -1107,13 +1106,7 @@ public final class MediaBrowser {
}
@Override
- public void onLoadChildren(String parentId, ParceledListSlice list) {
- onLoadChildrenWithOptions(parentId, list, null);
- }
-
- @Override
- public void onLoadChildrenWithOptions(String parentId, ParceledListSlice list,
- final Bundle options) {
+ public void onLoadChildren(String parentId, ParceledListSlice list, Bundle options) {
MediaBrowser mediaBrowser = mMediaBrowser.get();
if (mediaBrowser != null) {
mediaBrowser.onLoadChildren(this, parentId, list, options);
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index 7e3f2f8868fb..a8772076af97 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -23,7 +23,5 @@ oneway interface IMediaBrowserServiceCallbacks {
void onConnect(String root, in MediaSession.Token session, in Bundle extras);
@UnsupportedAppUsage
void onConnectFailed();
- void onLoadChildren(String mediaId, in ParceledListSlice list);
- void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list,
- in Bundle options);
+ void onLoadChildren(String mediaId, in ParceledListSlice list, in Bundle options);
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 06adf30a8303..39c7682a2a74 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -687,7 +687,7 @@ public abstract class MediaBrowserService extends Service {
final ParceledListSlice<MediaBrowser.MediaItem> pls =
filteredList == null ? null : new ParceledListSlice<>(filteredList);
try {
- connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options);
+ connection.callbacks.onLoadChildren(parentId, pls, options);
} catch (RemoteException ex) {
// The other side is in the process of crashing.
Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
diff --git a/non-updatable-api/removed.txt b/non-updatable-api/removed.txt
index ba05a1b89988..f2dfb84eb8fe 100644
--- a/non-updatable-api/removed.txt
+++ b/non-updatable-api/removed.txt
@@ -1,10 +1,6 @@
// Signature format: 2.0
package android.app {
- public class ActivityManager {
- method @Deprecated public static int getMaxNumPictureInPictureActions();
- }
-
public class Notification implements android.os.Parcelable {
method @Deprecated public String getChannel();
method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
diff --git a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
index 0a294246dfaa..09fbf7a59a8c 100644
--- a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
@@ -15,7 +15,6 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:fitsSystemWindows="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 2dc499c160c6..2c9788955bfa 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -22,12 +22,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <!-- TODO(b/151617493): replace marginBottom with insets. -->
<ViewStub android:id="@+id/notification_panel_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout="@layout/notification_panel_container"
- android:layout_marginBottom="@dimen/navigation_bar_height"/>
+ android:layout="@layout/notification_panel_container"/>
<ViewStub android:id="@+id/keyguard_stub"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 8accdd660f65..7b6dceb5fcd7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -128,9 +128,9 @@ public abstract class CarSystemUIModule {
@Singleton
@Provides
- static SystemWindows provideSystemWindows(Context context, DisplayController displayController,
+ static SystemWindows provideSystemWindows(DisplayController displayController,
IWindowManager wmService) {
- return new SystemWindows(context, displayController, wmService);
+ return new SystemWindows(displayController, wmService);
}
@Singleton
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 69766cc6c0d0..51a7245ea5c6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -141,6 +141,11 @@ public class CarKeyguardViewController extends OverlayViewController implements
}
@Override
+ protected boolean shouldShowNavigationBar() {
+ return true;
+ }
+
+ @Override
public void onFinishInflate() {
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 1eead62c042a..8d5843635e5f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,6 +16,8 @@
package com.android.systemui.car.notification;
+import static android.view.WindowInsets.Type.navigationBars;
+
import android.app.ActivityManager;
import android.car.Car;
import android.car.drivingstate.CarUxRestrictionsManager;
@@ -197,6 +199,16 @@ public class NotificationPanelViewController extends OverlayPanelViewController
}
@Override
+ protected boolean shouldShowStatusBar() {
+ return true;
+ }
+
+ @Override
+ protected int getInsetTypesToFit() {
+ return navigationBars();
+ }
+
+ @Override
protected boolean shouldShowHUN() {
return mEnableHeadsUpNotificationWhenNotificationShadeOpen;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 45f3d342fb6e..0d77c1341ffb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -91,6 +91,11 @@ public class UserSwitchTransitionViewController extends OverlayViewController {
R.integer.config_userSwitchTransitionViewShownTimeoutMs);
}
+ @Override
+ protected int getInsetTypesToFit() {
+ return 0;
+ }
+
/**
* Makes the user switch transition view appear and draws the content inside of it if a user
* that is different from the previous user is provided and if the dialog is not already
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 3969f92c690a..53deb9d9dc5d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -16,9 +16,12 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.statusBars;
+
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsets;
/**
* Owns a {@link View} that is present in SystemUIOverlayWindow.
@@ -140,9 +143,25 @@ public class OverlayViewController {
}
/**
+ * Returns {@code true} if status bar should be displayed over this view.
+ */
+ protected boolean shouldShowStatusBar() {
+ return false;
+ }
+
+ /**
* Returns {@code true} if this view should be hidden during the occluded state.
*/
protected boolean shouldShowWhenOccluded() {
return false;
}
+
+ /**
+ * Returns the insets types to fit to the sysui overlay window when this
+ * {@link OverlayViewController} is in the foreground.
+ */
+ @WindowInsets.Type.InsetsType
+ protected int getInsetTypesToFit() {
+ return statusBars();
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 8e9410964313..2494242c24f0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -16,13 +16,17 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+
import android.annotation.Nullable;
import android.util.Log;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsController;
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -48,10 +52,7 @@ public class OverlayViewGlobalStateController {
private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName();
private static final int UNKNOWN_Z_ORDER = -1;
private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
- private final CarNavigationBarController mCarNavigationBarController;
-
- private boolean mIsOccluded;
-
+ private final WindowInsetsController mWindowInsetsController;
@VisibleForTesting
Map<OverlayViewController, Integer> mZOrderMap;
@VisibleForTesting
@@ -60,14 +61,15 @@ public class OverlayViewGlobalStateController {
Set<OverlayViewController> mViewsHiddenForOcclusion;
@VisibleForTesting
OverlayViewController mHighestZOrder;
+ private boolean mIsOccluded;
@Inject
public OverlayViewGlobalStateController(
- CarNavigationBarController carNavigationBarController,
SystemUIOverlayWindowController systemUIOverlayWindowController) {
mSystemUIOverlayWindowController = systemUIOverlayWindowController;
mSystemUIOverlayWindowController.attach();
- mCarNavigationBarController = carNavigationBarController;
+ mWindowInsetsController =
+ mSystemUIOverlayWindowController.getBaseLayout().getWindowInsetsController();
mZOrderMap = new HashMap<>();
mZOrderVisibleSortedMap = new TreeMap<>();
mViewsHiddenForOcclusion = new HashSet<>();
@@ -115,7 +117,9 @@ public class OverlayViewGlobalStateController {
}
updateInternalsWhenShowingView(viewController);
+ refreshInsetTypesToFit();
refreshNavigationBarVisibility();
+ refreshStatusBarVisibility();
Log.d(TAG, "Content shown: " + viewController.getClass().getName());
debugLog();
@@ -185,7 +189,9 @@ public class OverlayViewGlobalStateController {
mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
refreshHighestZOrderWhenHidingView(viewController);
+ refreshInsetTypesToFit();
refreshNavigationBarVisibility();
+ refreshStatusBarVisibility();
if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(false);
@@ -208,10 +214,28 @@ public class OverlayViewGlobalStateController {
}
private void refreshNavigationBarVisibility() {
+ mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) {
- mCarNavigationBarController.showBars();
+ mWindowInsetsController.show(navigationBars());
} else {
- mCarNavigationBarController.hideBars();
+ mWindowInsetsController.hide(navigationBars());
+ }
+ }
+
+ private void refreshStatusBarVisibility() {
+ mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
+ if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowStatusBar()) {
+ mWindowInsetsController.show(statusBars());
+ } else {
+ mWindowInsetsController.hide(statusBars());
+ }
+ }
+
+ private void refreshInsetTypesToFit() {
+ if (mZOrderVisibleSortedMap.isEmpty()) {
+ setFitInsetsTypes(statusBars());
+ } else {
+ setFitInsetsTypes(mHighestZOrder.getInsetTypesToFit());
}
}
@@ -224,6 +248,10 @@ public class OverlayViewGlobalStateController {
mSystemUIOverlayWindowController.setWindowVisible(visible);
}
+ private void setFitInsetsTypes(@InsetsType int types) {
+ mSystemUIOverlayWindowController.setFitInsetsTypes(types);
+ }
+
/**
* Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
* sysui overlay window.
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
index bcd96f63a2b4..029bd3702afe 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
@@ -25,6 +25,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.WindowManager;
import com.android.systemui.R;
@@ -99,7 +100,6 @@ public class SystemUIOverlayWindowController implements
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
- mLp.setFitInsetsTypes(/* types= */ 0);
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("SystemUIOverlayWindow");
mLp.packageName = mContext.getPackageName();
@@ -110,6 +110,12 @@ public class SystemUIOverlayWindowController implements
setWindowVisible(false);
}
+ /** Sets the types of insets to fit. Note: This should be rarely used. */
+ public void setFitInsetsTypes(@WindowInsets.Type.InsetsType int types) {
+ mLpChanged.setFitInsetsTypes(types);
+ updateWindow();
+ }
+
/** Sets the window to the visible state. */
public void setWindowVisible(boolean visible) {
mVisible = visible;
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
index 63f8c72b354a..5c80202ba592 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -16,12 +16,14 @@
package com.android.systemui.wm;
+import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
import android.view.InsetsController;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -32,7 +34,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import javax.inject.Inject;
@@ -50,29 +51,32 @@ public class DisplaySystemBarsController extends DisplayImeController {
private static final String TAG = "DisplaySystemBarsController";
private SparseArray<PerDisplay> mPerDisplaySparseArray;
+ private final Context mContext;
@Inject
public DisplaySystemBarsController(
- SystemWindows syswin,
+ Context context,
+ IWindowManager wmService,
DisplayController displayController,
@Main Handler mainHandler,
TransactionPool transactionPool) {
- super(syswin, displayController, mainHandler, transactionPool);
+ super(wmService, displayController, mainHandler, transactionPool);
+ mContext = context;
}
@Override
public void onDisplayAdded(int displayId) {
PerDisplay pd = new PerDisplay(displayId);
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+ mWmService.setDisplayWindowInsetsController(displayId, pd);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + displayId);
}
// Lazy loading policy control filters instead of during boot.
if (mPerDisplaySparseArray == null) {
mPerDisplaySparseArray = new SparseArray<>();
- BarControlPolicy.reloadFromSetting(mSystemWindows.mContext);
- BarControlPolicy.registerContentObserver(mSystemWindows.mContext, mHandler, () -> {
+ BarControlPolicy.reloadFromSetting(mContext);
+ BarControlPolicy.registerContentObserver(mContext, mHandler, () -> {
int size = mPerDisplaySparseArray.size();
for (int i = 0; i < size; i++) {
mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets();
@@ -85,7 +89,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
@Override
public void onDisplayRemoved(int displayId) {
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+ mWmService.setDisplayWindowInsetsController(displayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
}
@@ -155,7 +159,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
showInsets(barVisibilities[0], /* fromIme= */ false);
hideInsets(barVisibilities[1], /* fromIme= */ false);
try {
- mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to update window manager service.");
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index 20f9bc8ec1cb..ff286650ea50 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -16,9 +16,14 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -28,19 +33,18 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarSystemUiTest;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
@@ -58,8 +62,6 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
private ViewGroup mBaseLayout;
@Mock
- private CarNavigationBarController mCarNavigationBarController;
- @Mock
private SystemUIOverlayWindowController mSystemUIOverlayWindowController;
@Mock
private OverlayViewMediator mOverlayViewMediator;
@@ -71,18 +73,22 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
private OverlayPanelViewController mOverlayPanelViewController;
@Mock
private Runnable mRunnable;
+ @Mock
+ private WindowInsetsController mWindowInsetsController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
- mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
- R.layout.overlay_view_global_state_controller_test, /* root= */ null);
+ mBaseLayout = spy((ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.overlay_view_global_state_controller_test, /* root= */ null));
+
+ when(mBaseLayout.getWindowInsetsController()).thenReturn(mWindowInsetsController);
when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
- mCarNavigationBarController, mSystemUIOverlayWindowController);
+ mSystemUIOverlayWindowController);
verify(mSystemUIOverlayWindowController).attach();
}
@@ -108,7 +114,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -118,7 +124,37 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -168,10 +204,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -183,7 +220,46 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -216,10 +292,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -231,7 +308,44 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -402,10 +516,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -418,7 +533,48 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(statusBars());
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -441,10 +597,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -457,7 +614,48 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -479,7 +677,27 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_viewControllerOnlyShown_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_viewControllerOnlyShown_insetsAdjustedToDefault() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(statusBars());
}
@Test
@@ -615,7 +833,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
private void setOverlayViewControllerAsShowing(OverlayViewController overlayViewController) {
mOverlayViewGlobalStateController.showView(overlayViewController, /* show= */ null);
- Mockito.reset(mCarNavigationBarController, mSystemUIOverlayWindowController);
+ reset(mSystemUIOverlayWindowController);
when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
}
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
index 0f28d38f7878..391f75e35382 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
@@ -34,7 +34,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import org.junit.Before;
@@ -53,8 +52,6 @@ public class DisplaySystemBarsControllerTest extends SysuiTestCase {
private static final int DISPLAY_ID = 1;
@Mock
- private SystemWindows mSystemWindows;
- @Mock
private IWindowManager mIWindowManager;
@Mock
private DisplayController mDisplayController;
@@ -66,11 +63,10 @@ public class DisplaySystemBarsControllerTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mSystemWindows.mContext = mContext;
- mSystemWindows.mWmService = mIWindowManager;
mController = new DisplaySystemBarsController(
- mSystemWindows,
+ mContext,
+ mIWindowManager,
mDisplayController,
mHandler,
mTransactionPool
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 16ef59f201f1..02f4457bffdb 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -29,6 +29,7 @@ import android.text.Html;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
+import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -75,6 +76,14 @@ public class DeviceChooserActivity extends Activity {
mDeviceListView = findViewById(R.id.device_list);
final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter;
mDeviceListView.setAdapter(adapter);
+ mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
+ getService().mSelectedDevice =
+ (DeviceFilterPair) adapterView.getItemAtPosition(pos);
+ adapter.notifyDataSetChanged();
+ }
+ });
adapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
@@ -157,4 +166,4 @@ public class DeviceChooserActivity extends Activity {
new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
finish();
}
-} \ No newline at end of file
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 7aa997e39307..bcaee367b03c 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -349,10 +349,6 @@ public class DeviceDiscoveryService extends Service {
? WIFI_ICON
: BLUETOOTH_ICON,
null, null, null);
- textView.setOnClickListener((view) -> {
- mSelectedDevice = device;
- notifyDataSetChanged();
- });
}
//TODO move to a layout file
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index d480ff63d8e3..508cbfccffe9 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -140,8 +140,8 @@
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string>
- <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Aplicações removidas"</string>
- <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Aplicações e utilizadores removidos"</string>
+ <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Apps removidas"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Apps e utilizadores removidos"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"Atualizações do sistema"</string>
<string name="tether_settings_title_usb" msgid="3728686573430917722">"Ligação USB"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Hotspot portátil"</string>
@@ -365,7 +365,7 @@
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração de animação"</string>
<string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular apresentações secundárias"</string>
- <string name="debug_applications_category" msgid="5394089406638954196">"Aplicações"</string>
+ <string name="debug_applications_category" msgid="5394089406638954196">"Apps"</string>
<string name="immediately_destroy_activities" msgid="1826287490705167403">"Não manter atividades"</string>
<string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Destruir atividades assim que o utilizador sair"</string>
<string name="app_process_limit_title" msgid="8361367869453043007">"Limite do processo em 2º plano"</string>
@@ -396,7 +396,7 @@
<item msgid="4548987861791236754">"Cores naturais e realistas"</item>
<item msgid="1282170165150762976">"Cores otimizadas para conteúdos digitais"</item>
</string-array>
- <string name="inactive_apps_title" msgid="5372523625297212320">"Aplicações em espera"</string>
+ <string name="inactive_apps_title" msgid="5372523625297212320">"Apps em espera"</string>
<string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Toque para ativar/desativar."</string>
<string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Toque para ativar/desativar."</string>
<string name="standby_bucket_summary" msgid="5128193447550429600">"Estado do Modo de espera das apps:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
index 87dd58e4f0ed..213bb923db65 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.bubbles.BubbleManageEducationView
+<com.android.systemui.bubbles.ManageEducationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -87,4 +87,4 @@
/>
</LinearLayout>
</LinearLayout>
-</com.android.systemui.bubbles.BubbleManageEducationView>
+</com.android.systemui.bubbles.ManageEducationView>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
new file mode 100644
index 000000000000..769a344eedac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
@@ -0,0 +1,81 @@
+/*
+ * 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.accessibility;
+
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManager;
+import android.util.SparseArray;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Supplies the instance with given display Id. It generates a new instance if the corresponding
+ * one is not existed. It should run in single thread to avoid race conditions.
+ *
+ * @param <T> the type of results supplied by {@link #createInstance(Display)}.
+ */
+abstract class DisplayIdIndexSupplier<T> {
+
+ private final SparseArray<T> mSparseArray = new SparseArray<>();
+ private final DisplayManager mDisplayManager;
+
+ /**
+ * @param displayManager DisplayManager
+ */
+ DisplayIdIndexSupplier(DisplayManager displayManager) {
+ mDisplayManager = displayManager;
+ }
+
+ /**
+ * @param displayId the logical display Id
+ * @return {@code null} if the given display id is invalid
+ */
+ @Nullable
+ public T get(int displayId) {
+ T instance = mSparseArray.get(displayId);
+ if (instance != null) {
+ return instance;
+ }
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ return null;
+ }
+ instance = createInstance(display);
+ mSparseArray.put(displayId, instance);
+ return instance;
+ }
+
+ @NonNull
+ protected abstract T createInstance(Display display);
+
+ /**
+ * Removes the instance with given display Id.
+ *
+ * @param displayId the logical display id
+ */
+ public void remove(int displayId) {
+ mSparseArray.remove(displayId);
+ }
+
+ /**
+ * Clears all elements.
+ */
+ public void clear() {
+ mSparseArray.clear();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 398a2c9c9d41..68a0a65ef50f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -30,6 +30,7 @@ import com.android.systemui.R;
/**
* Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
* {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
+ * The button UI would automatically be dismissed after displaying for a period of time.
*/
class MagnificationModeSwitch {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
index e73ff13ceac1..ffc70bcf63d0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
@@ -19,31 +19,33 @@ package com.android.systemui.accessibility;
import android.annotation.MainThread;
import android.content.Context;
import android.hardware.display.DisplayManager;
-import android.util.Log;
-import android.util.SparseArray;
import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
+
import javax.inject.Singleton;
/**
- * Class to control magnification mode switch button. Shows the button UI when both full-screen
- * and window magnification mode are capable, and when the magnification scale is changed. And
- * the button UI would automatically be dismissed after displaying for a period of time.
+ * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following
+ * conditions:
+ * <ol>
+ * <li> Both full-screen and window magnification mode are capable.</li>
+ * <li> The magnification scale is changed by a user.</li>
+ * <ol>
*/
@Singleton
public class ModeSwitchesController {
- private static final String TAG = "ModeSwitchesController";
-
- private final Context mContext;
- private final DisplayManager mDisplayManager;
-
- private final SparseArray<MagnificationModeSwitch> mDisplaysToSwitches =
- new SparseArray<>();
+ private final SwitchSupplier mSwitchSupplier;
public ModeSwitchesController(Context context) {
- mContext = context;
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mSwitchSupplier = new SwitchSupplier(context,
+ context.getSystemService(DisplayManager.class));
+ }
+
+ @VisibleForTesting
+ ModeSwitchesController(SwitchSupplier switchSupplier) {
+ mSwitchSupplier = switchSupplier;
}
/**
@@ -52,20 +54,17 @@ public class ModeSwitchesController {
*
* @param displayId The logical display id
* @param mode The magnification mode
- *
* @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
* @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
*/
@MainThread
void showButton(int displayId, int mode) {
- if (mDisplaysToSwitches.get(displayId) == null) {
- final MagnificationModeSwitch magnificationModeSwitch =
- createMagnificationSwitchController(displayId);
- if (magnificationModeSwitch == null) {
- return;
- }
+ final MagnificationModeSwitch magnificationModeSwitch =
+ mSwitchSupplier.get(displayId);
+ if (magnificationModeSwitch == null) {
+ return;
}
- mDisplaysToSwitches.get(displayId).showButton(mode);
+ magnificationModeSwitch.showButton(mode);
}
/**
@@ -74,30 +73,34 @@ public class ModeSwitchesController {
* @param displayId The logical display id
*/
void removeButton(int displayId) {
- if (mDisplaysToSwitches.get(displayId) == null) {
+ final MagnificationModeSwitch magnificationModeSwitch =
+ mSwitchSupplier.get(displayId);
+ if (magnificationModeSwitch == null) {
return;
}
- mDisplaysToSwitches.get(displayId).removeButton();
+ magnificationModeSwitch.removeButton();
}
- private MagnificationModeSwitch createMagnificationSwitchController(int displayId) {
- if (mDisplayManager.getDisplay(displayId) == null) {
- Log.w(TAG, "createMagnificationSwitchController displayId is invalid.");
- return null;
+ @VisibleForTesting
+ static class SwitchSupplier extends DisplayIdIndexSupplier<MagnificationModeSwitch> {
+
+ private final Context mContext;
+
+ /**
+ * @param context Context
+ * @param displayManager DisplayManager
+ */
+ SwitchSupplier(Context context, DisplayManager displayManager) {
+ super(displayManager);
+ mContext = context;
}
- final MagnificationModeSwitch
- magnificationModeSwitch = new MagnificationModeSwitch(
- getDisplayContext(displayId));
- mDisplaysToSwitches.put(displayId, magnificationModeSwitch);
- return magnificationModeSwitch;
- }
- private Context getDisplayContext(int displayId) {
- final Display display = mDisplayManager.getDisplay(displayId);
- final Context context = (displayId == Display.DEFAULT_DISPLAY)
- ? mContext
- : mContext.createDisplayContext(display);
- return context;
+ @Override
+ protected MagnificationModeSwitch createInstance(Display display) {
+ final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY)
+ ? mContext
+ : mContext.createDisplayContext(display);
+ return new MagnificationModeSwitch(context);
+ }
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 980e4c0fd333..361ea674cead 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -29,7 +29,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
@@ -39,12 +38,10 @@ import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.IFingerprintService;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.Log;
import android.view.WindowManager;
@@ -247,6 +244,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
IActivityTaskManager getActivityTaskManager() {
return ActivityTaskManager.getService();
}
+
+ FingerprintManager getFingerprintManager(Context context) {
+ return context.getSystemService(FingerprintManager.class);
+ }
}
@Inject
@@ -273,7 +274,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mActivityTaskManager = mInjector.getActivityTaskManager();
- final FingerprintManager fpm = mContext.getSystemService(FingerprintManager.class);
+ final FingerprintManager fpm = mInjector.getFingerprintManager(mContext);
if (fpm != null && fpm.isHardwareDetected()) {
final List<FingerprintSensorProperties> fingerprintSensorProperties =
fpm.getSensorProperties();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java
deleted file mode 100644
index 9db371e487c7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
-import com.android.systemui.R;
-
-/** Dismiss view that contains a scrim gradient, as well as a dismiss icon, text, and circle. */
-public class BubbleDismissView extends FrameLayout {
- /** Duration for animations involving the dismiss target text/icon. */
- private static final int DISMISS_TARGET_ANIMATION_BASE_DURATION = 150;
- private static final float SCALE_FOR_POP = 1.2f;
- private static final float SCALE_FOR_DISMISS = 0.9f;
-
- private LinearLayout mDismissTarget;
- private ImageView mDismissIcon;
- private View mDismissCircle;
-
- private SpringAnimation mDismissTargetAlphaSpring;
- private SpringAnimation mDismissTargetVerticalSpring;
-
- public BubbleDismissView(Context context) {
- super(context);
- setVisibility(GONE);
-
- LayoutInflater.from(context).inflate(R.layout.bubble_dismiss_target, this, true);
- mDismissTarget = findViewById(R.id.bubble_dismiss_icon_container);
- mDismissIcon = findViewById(R.id.bubble_dismiss_close_icon);
- mDismissCircle = findViewById(R.id.bubble_dismiss_circle);
-
- // Set up the basic target area animations. These are very simple animations that don't need
- // fancy interpolators.
- final AccelerateDecelerateInterpolator interpolator =
- new AccelerateDecelerateInterpolator();
- mDismissIcon.animate()
- .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION)
- .setInterpolator(interpolator);
- mDismissCircle.animate()
- .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION / 2)
- .setInterpolator(interpolator);
-
- mDismissTargetAlphaSpring =
- new SpringAnimation(mDismissTarget, DynamicAnimation.ALPHA)
- .setSpring(new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_LOW)
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
- mDismissTargetVerticalSpring =
- new SpringAnimation(mDismissTarget, DynamicAnimation.TRANSLATION_Y)
- .setSpring(new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_MEDIUM)
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
-
- mDismissTargetAlphaSpring.addEndListener((anim, canceled, alpha, velocity) -> {
- // Since DynamicAnimations end when they're 'nearly' done, we can't rely on alpha being
- // exactly zero when this listener is triggered. However, if it's less than 50% we can
- // safely assume it was animating out rather than in.
- if (alpha < 0.5f) {
- // If the alpha spring was animating the view out, set it to GONE when it's done.
- setVisibility(INVISIBLE);
- }
- });
- }
-
- /** Springs in the dismiss target. */
- void springIn() {
- setVisibility(View.VISIBLE);
-
- // Fade in the dismiss target icon.
- mDismissIcon.animate()
- .setDuration(50)
- .scaleX(1f)
- .scaleY(1f)
- .alpha(1f);
- mDismissTarget.setAlpha(0f);
- mDismissTargetAlphaSpring.animateToFinalPosition(1f);
-
- // Spring up the dismiss target.
- mDismissTarget.setTranslationY(mDismissTarget.getHeight() / 2f);
- mDismissTargetVerticalSpring.animateToFinalPosition(0);
-
- mDismissCircle.setAlpha(0f);
- mDismissCircle.setScaleX(SCALE_FOR_POP);
- mDismissCircle.setScaleY(SCALE_FOR_POP);
-
- // Fade in circle and reduce size.
- mDismissCircle.animate()
- .alpha(1f)
- .scaleX(1f)
- .scaleY(1f);
- }
-
- /** Springs out the dismiss target. */
- void springOut() {
- // Fade out the target icon.
- mDismissIcon.animate()
- .setDuration(50)
- .scaleX(SCALE_FOR_DISMISS)
- .scaleY(SCALE_FOR_DISMISS)
- .alpha(0f);
-
- // Fade out the target.
- mDismissTargetAlphaSpring.animateToFinalPosition(0f);
-
- // Spring the target down a bit.
- mDismissTargetVerticalSpring.animateToFinalPosition(mDismissTarget.getHeight() / 2f);
-
- // Pop out the circle.
- mDismissCircle.animate()
- .scaleX(SCALE_FOR_DISMISS)
- .scaleY(SCALE_FOR_DISMISS)
- .alpha(0f);
- }
-
- /** Returns the Y value of the center of the dismiss target. */
- float getDismissTargetCenterY() {
- return getTop() + mDismissTarget.getTop() + mDismissTarget.getHeight() / 2f;
- }
-
- /** Returns the dismiss target, which contains the text/icon and any added padding. */
- View getDismissTarget() {
- return mDismissTarget;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
deleted file mode 100644
index 86244ba5248a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
+++ /dev/null
@@ -1,106 +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.systemui.bubbles;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
-
-/**
- * Educational view to highlight the manage button that allows a user to configure the settings
- * for the bubble. Shown only the first time a user expands a bubble.
- */
-public class BubbleManageEducationView extends LinearLayout {
-
- private View mManageView;
- private TextView mTitleTextView;
- private TextView mDescTextView;
-
- public BubbleManageEducationView(Context context) {
- this(context, null);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mManageView = findViewById(R.id.manage_education_view);
- mTitleTextView = findViewById(R.id.user_education_title);
- mDescTextView = findViewById(R.id.user_education_description);
-
- final TypedArray ta = mContext.obtainStyledAttributes(
- new int[] {android.R.attr.colorAccent,
- android.R.attr.textColorPrimaryInverse});
- final int bgColor = ta.getColor(0, Color.BLACK);
- int textColor = ta.getColor(1, Color.WHITE);
- ta.recycle();
-
- textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
- mTitleTextView.setTextColor(textColor);
- mDescTextView.setTextColor(textColor);
- }
-
- /**
- * Specifies the position for the manage view.
- */
- public void setManageViewPosition(int x, int y) {
- mManageView.setTranslationX(x);
- mManageView.setTranslationY(y);
- }
-
- /**
- * @return the height of the view that shows the educational text and pointer.
- */
- public int getManageViewHeight() {
- return mManageView.getHeight();
- }
-
- @Override
- public void setLayoutDirection(int direction) {
- super.setLayoutDirection(direction);
- if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl);
- mTitleTextView.setGravity(Gravity.RIGHT);
- mDescTextView.setGravity(Gravity.RIGHT);
- } else {
- mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg);
- mTitleTextView.setGravity(Gravity.LEFT);
- mDescTextView.setGravity(Gravity.LEFT);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
deleted file mode 100644
index bb9d1095a37a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ /dev/null
@@ -1,179 +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.systemui.bubbles;
-
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.View.GONE;
-
-import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
-import android.util.PathParser;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-/**
- * Class for showing aged out bubbles.
- */
-public class BubbleOverflow implements BubbleViewProvider {
- public static final String KEY = "Overflow";
-
- private BadgedImageView mOverflowBtn;
- private BubbleExpandedView mExpandedView;
- private LayoutInflater mInflater;
- private Context mContext;
- private Bitmap mIcon;
- private Path mPath;
- private int mBitmapSize;
- private int mIconBitmapSize;
- private int mDotColor;
-
- public BubbleOverflow(Context context) {
- mContext = context;
- mInflater = LayoutInflater.from(context);
- }
-
- void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
- updateDimensions();
- mExpandedView = (BubbleExpandedView) mInflater.inflate(
- R.layout.bubble_expanded_view, parentViewGroup /* root */,
- false /* attachToRoot */);
- mExpandedView.setOverflow(true);
- mExpandedView.setStackView(stackView);
- mExpandedView.applyThemeAttrs();
- updateIcon(mContext, parentViewGroup);
- }
-
- void updateDimensions() {
- mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
- mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.bubble_overflow_icon_bitmap_size);
- if (mExpandedView != null) {
- mExpandedView.updateDimensions();
- }
- }
-
- void updateIcon(Context context, ViewGroup parentViewGroup) {
- mContext = context;
- mInflater = LayoutInflater.from(context);
- mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button,
- parentViewGroup /* root */,
- false /* attachToRoot */);
- mOverflowBtn.setContentDescription(mContext.getResources().getString(
- R.string.bubble_overflow_button_content_description));
- Resources res = mContext.getResources();
-
- // Set color for button icon and dot
- TypedValue typedValue = new TypedValue();
- mContext.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
- int colorAccent = mContext.getColor(typedValue.resourceId);
- mOverflowBtn.getDrawable().setTint(colorAccent);
- mDotColor = colorAccent;
-
- // Set color for button and activity background
- ColorDrawable bg = new ColorDrawable(res.getColor(R.color.bubbles_light));
- final int mode = res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
- if (mode == Configuration.UI_MODE_NIGHT_YES) {
- bg = new ColorDrawable(res.getColor(R.color.bubbles_dark));
- }
-
- // Apply icon inset
- InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(),
- mBitmapSize - mIconBitmapSize /* inset */);
- AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(bg, fg);
-
- BubbleIconFactory iconFactory = new BubbleIconFactory(mContext);
- mIcon = iconFactory.createBadgedIconBitmap(adaptiveIconDrawable,
- null /* user */,
- true /* shrinkNonAdaptiveIcons */).icon;
-
- // Get path with dot location
- float scale = iconFactory.getNormalizer().getScale(mOverflowBtn.getDrawable(),
- null /* outBounds */, null /* path */, null /* outMaskShape */);
- float radius = DEFAULT_PATH_SIZE / 2f;
- mPath = PathParser.createPathFromPathData(
- mContext.getResources().getString(com.android.internal.R.string.config_icon_mask));
- Matrix matrix = new Matrix();
- matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
- radius /* pivot y */);
- mPath.transform(matrix);
-
- mOverflowBtn.setRenderedBubble(this);
- }
-
- void setVisible(int visible) {
- mOverflowBtn.setVisibility(visible);
- }
-
- @Override
- public BubbleExpandedView getExpandedView() {
- return mExpandedView;
- }
-
- @Override
- public int getDotColor() {
- return mDotColor;
- }
-
- @Override
- public Bitmap getBadgedImage() {
- return mIcon;
- }
-
- @Override
- public boolean showDot() {
- return false;
- }
-
- @Override
- public Path getDotPath() {
- return mPath;
- }
-
- @Override
- public void setContentVisibility(boolean visible) {
- mExpandedView.setContentVisibility(visible);
- }
-
- @Override
- public View getIconView() {
- return mOverflowBtn;
- }
-
- @Override
- public String getKey() {
- return BubbleOverflow.KEY;
- }
-
- @Override
- public int getDisplayId() {
- return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
new file mode 100644
index 000000000000..155b71b99ff9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -0,0 +1,163 @@
+/*
+ * 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.bubbles
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Matrix
+import android.graphics.Path
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.InsetDrawable
+import android.util.PathParser
+import android.util.TypedValue
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import com.android.systemui.R
+
+class BubbleOverflow(
+ private val context: Context,
+ private val stack: BubbleStackView
+) : BubbleViewProvider {
+
+ private var bitmap: Bitmap? = null
+ private var dotPath: Path? = null
+ private var bitmapSize = 0
+ private var iconBitmapSize = 0
+ private var dotColor = 0
+
+ private val inflater: LayoutInflater = LayoutInflater.from(context)
+ private val expandedView: BubbleExpandedView = inflater
+ .inflate(R.layout.bubble_expanded_view, null /* root */, false /* attachToRoot */)
+ as BubbleExpandedView
+ private val overflowBtn: BadgedImageView = inflater
+ .inflate(R.layout.bubble_overflow_button, null /* root */, false /* attachToRoot */)
+ as BadgedImageView
+ init {
+ updateResources()
+ with(expandedView) {
+ setOverflow(true)
+ setStackView(stack)
+ applyThemeAttrs()
+ }
+ with(overflowBtn) {
+ setContentDescription(context.resources.getString(
+ R.string.bubble_overflow_button_content_description))
+ updateBtnTheme()
+ }
+ }
+
+ fun update() {
+ updateResources()
+ expandedView.applyThemeAttrs()
+ // Apply inset and new style to fresh icon drawable.
+ overflowBtn.setImageResource(R.drawable.ic_bubble_overflow_button)
+ updateBtnTheme()
+ }
+
+ fun updateResources() {
+ bitmapSize = context.resources.getDimensionPixelSize(R.dimen.bubble_bitmap_size)
+ iconBitmapSize = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_overflow_icon_bitmap_size)
+ val bubbleSize = context.resources.getDimensionPixelSize(R.dimen.individual_bubble_size)
+ overflowBtn.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
+ expandedView.updateDimensions()
+ }
+
+ fun updateBtnTheme() {
+ val res = context.resources
+
+ // Set overflow button accent color, dot color
+ val typedValue = TypedValue()
+ context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true)
+
+ val colorAccent = res.getColor(typedValue.resourceId)
+ overflowBtn.getDrawable()?.setTint(colorAccent)
+ dotColor = colorAccent
+
+ // Set button and activity background color
+ val nightMode = (res.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ == Configuration.UI_MODE_NIGHT_YES)
+ val bg = ColorDrawable(res.getColor(
+ if (nightMode) R.color.bubbles_dark else R.color.bubbles_light))
+
+ // Set button icon
+ val iconFactory = BubbleIconFactory(context)
+ val fg = InsetDrawable(overflowBtn.getDrawable(),
+ bitmapSize - iconBitmapSize /* inset */)
+ bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
+ null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+
+ // Set dot path
+ dotPath = PathParser.createPathFromPathData(
+ res.getString(com.android.internal.R.string.config_icon_mask))
+ val scale = iconFactory.normalizer.getScale(overflowBtn.getDrawable(),
+ null /* outBounds */, null /* path */, null /* outMaskShape */)
+ val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
+ val matrix = Matrix()
+ matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+ radius /* pivot y */)
+ dotPath?.transform(matrix)
+ overflowBtn.setRenderedBubble(this)
+ }
+
+ fun setVisible(visible: Int) {
+ overflowBtn.visibility = visible
+ }
+
+ override fun getExpandedView(): BubbleExpandedView? {
+ return expandedView
+ }
+
+ override fun getDotColor(): Int {
+ return dotColor
+ }
+
+ override fun getBadgedImage(): Bitmap? {
+ return bitmap
+ }
+
+ override fun showDot(): Boolean {
+ return false
+ }
+
+ override fun getDotPath(): Path? {
+ return dotPath
+ }
+
+ override fun setContentVisibility(visible: Boolean) {
+ expandedView.setContentVisibility(visible)
+ }
+
+ override fun getIconView(): View? {
+ return overflowBtn
+ }
+
+ override fun getKey(): String {
+ return KEY
+ }
+
+ override fun getDisplayId(): Int {
+ return expandedView.virtualDisplayId
+ }
+
+ companion object {
+ @JvmField val KEY = "Overflow"
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index f02945ef843a..ea12c9598b91 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -48,7 +48,6 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
-import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
@@ -95,7 +94,6 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.RelativeTouchListener;
import com.android.systemui.util.animation.PhysicsAnimator;
@@ -118,7 +116,7 @@ public class BubbleStackView extends FrameLayout
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
/** Animation durations for bubble stack user education views. **/
- private static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
+ static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40;
/** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
@@ -139,9 +137,6 @@ public class BubbleStackView extends FrameLayout
/** Percent to darken the bubbles when they're in the dismiss target. */
private static final float DARKEN_PERCENT = 0.3f;
- /** Duration of the dismiss scrim fading in/out. */
- private static final int DISMISS_TRANSITION_DURATION_MS = 200;
-
/** How long to wait, in milliseconds, before hiding the flyout. */
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
@@ -300,7 +295,7 @@ public class BubbleStackView extends FrameLayout
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Stack view state:");
pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress);
- pw.print(" showingDismiss: "); pw.println(mShowingDismiss);
+ pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing());
pw.print(" isExpansionAnimating: "); pw.println(mIsExpansionAnimating);
pw.print(" expandedContainerVis: "); pw.println(mExpandedViewContainer.getVisibility());
pw.print(" expandedContainerAlpha: "); pw.println(mExpandedViewContainer.getAlpha());
@@ -347,7 +342,6 @@ public class BubbleStackView extends FrameLayout
private boolean mViewUpdatedRequested = false;
private boolean mIsExpansionAnimating = false;
private boolean mIsBubbleSwitchAnimating = false;
- private boolean mShowingDismiss = false;
/** The view to desaturate/darken when magneted to the dismiss target. */
@Nullable private View mDesaturateAndDarkenTargetView;
@@ -465,7 +459,7 @@ public class BubbleStackView extends FrameLayout
if (wasFlungOut) {
mExpandedAnimationController.snapBubbleBack(
mExpandedAnimationController.getDraggedOutBubble(), velX, velY);
- hideDismissTarget();
+ mDismissView.hide();
} else {
mExpandedAnimationController.onUnstuckFromTarget();
}
@@ -479,9 +473,9 @@ public class BubbleStackView extends FrameLayout
mExpandedAnimationController.dismissDraggedOutBubble(
mExpandedAnimationController.getDraggedOutBubble() /* bubble */,
- mDismissTargetContainer.getHeight() /* translationYBy */,
+ mDismissView.getHeight() /* translationYBy */,
BubbleStackView.this::dismissMagnetizedObject /* after */);
- hideDismissTarget();
+ mDismissView.hide();
}
};
@@ -502,7 +496,7 @@ public class BubbleStackView extends FrameLayout
if (wasFlungOut) {
mStackAnimationController.flingStackThenSpringToEdge(
mStackAnimationController.getStackPosition().x, velX, velY);
- hideDismissTarget();
+ mDismissView.hide();
} else {
mStackAnimationController.onUnstuckFromTarget();
}
@@ -511,14 +505,14 @@ public class BubbleStackView extends FrameLayout
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
mStackAnimationController.animateStackDismissal(
- mDismissTargetContainer.getHeight() /* translationYBy */,
+ mDismissView.getHeight() /* translationYBy */,
() -> {
resetDesaturationAndDarken();
dismissMagnetizedObject();
}
);
- hideDismissTarget();
+ mDismissView.hide();
}
};
@@ -639,7 +633,7 @@ public class BubbleStackView extends FrameLayout
}
// Show the dismiss target, if we haven't already.
- springInDismissTargetMaybe();
+ mDismissView.show();
// First, see if the magnetized object consumes the event - if so, we shouldn't move the
// bubble since it's stuck to the target.
@@ -681,7 +675,7 @@ public class BubbleStackView extends FrameLayout
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
- hideDismissTarget();
+ mDismissView.hide();
}
mIsDraggingStack = false;
@@ -743,12 +737,7 @@ public class BubbleStackView extends FrameLayout
}
};
- private View mDismissTargetCircle;
- private ViewGroup mDismissTargetContainer;
- private PhysicsAnimator<View> mDismissTargetAnimator;
- private PhysicsAnimator.SpringConfig mDismissTargetSpring = new PhysicsAnimator.SpringConfig(
- SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
-
+ private DismissView mDismissView;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
@Nullable
@@ -759,7 +748,7 @@ public class BubbleStackView extends FrameLayout
private View mUserEducationView;
private boolean mShouldShowManageEducation;
- private BubbleManageEducationView mManageEducationView;
+ private ManageEducationView mManageEducationView;
private boolean mAnimatingManageEducationAway;
private ViewGroup mManageMenu;
@@ -866,34 +855,8 @@ public class BubbleStackView extends FrameLayout
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring);
- final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
- mDismissTargetCircle = new DismissCircleView(context);
- final FrameLayout.LayoutParams newParams =
- new FrameLayout.LayoutParams(targetSize, targetSize);
- newParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
- mDismissTargetCircle.setLayoutParams(newParams);
- mDismissTargetAnimator = PhysicsAnimator.getInstance(mDismissTargetCircle);
-
- mDismissTargetContainer = new FrameLayout(context);
- mDismissTargetContainer.setLayoutParams(new FrameLayout.LayoutParams(
- MATCH_PARENT,
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
- Gravity.BOTTOM));
-
- final int bottomMargin =
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin);
- mDismissTargetContainer.setPadding(0, 0, 0, bottomMargin);
- mDismissTargetContainer.setClipToPadding(false);
- mDismissTargetContainer.setClipChildren(false);
- mDismissTargetContainer.addView(mDismissTargetCircle);
- mDismissTargetContainer.setVisibility(View.INVISIBLE);
- mDismissTargetContainer.setBackgroundResource(
- R.drawable.floating_dismiss_gradient_transition);
- addView(mDismissTargetContainer);
-
- // Start translated down so the target springs up.
- mDismissTargetCircle.setTranslationY(
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height));
+ mDismissView = new DismissView(context);
+ addView(mDismissView);
final ContentResolver contentResolver = getContext().getContentResolver();
final int dismissRadius = Settings.Secure.getInt(
@@ -901,13 +864,23 @@ public class BubbleStackView extends FrameLayout
// Save the MagneticTarget instance for the newly set up view - we'll add this to the
// MagnetizedObjects.
- mMagneticTarget = new MagnetizedObject.MagneticTarget(mDismissTargetCircle, dismissRadius);
+ mMagneticTarget = new MagnetizedObject.MagneticTarget(
+ mDismissView.getCircle(), dismissRadius);
setClipChildren(false);
setFocusable(true);
mBubbleContainer.bringToFront();
- setUpOverflow();
+ mBubbleOverflow = new BubbleOverflow(getContext(), this);
+ mBubbleContainer.addView(mBubbleOverflow.getIconView(),
+ mBubbleContainer.getChildCount() /* index */,
+ new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ updateOverflow();
+ mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
+ setSelectedBubble(mBubbleOverflow);
+ showManageMenu(false);
+ });
mOnImeVisibilityChanged = onImeVisibilityChanged;
mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
@@ -933,7 +906,7 @@ public class BubbleStackView extends FrameLayout
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
mStackAnimationController.updateResources(mOrientation);
- mBubbleOverflow.updateDimensions();
+ mBubbleOverflow.updateResources();
// Need to update the padding around the view
WindowInsets insets = getRootWindowInsets();
@@ -1162,12 +1135,9 @@ public class BubbleStackView extends FrameLayout
Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation);
}
if (mShouldShowManageEducation) {
- mManageEducationView = (BubbleManageEducationView)
- mInflater.inflate(R.layout.bubbles_manage_button_education, this,
+ mManageEducationView = (ManageEducationView)
+ mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */,
false /* attachToRoot */);
- mManageEducationView.setVisibility(GONE);
- mManageEducationView.setElevation(mBubbleElevation);
- mManageEducationView.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
addView(mManageEducationView);
}
}
@@ -1187,32 +1157,21 @@ public class BubbleStackView extends FrameLayout
addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
}
- private void setUpOverflow() {
- int overflowBtnIndex = 0;
- if (mBubbleOverflow == null) {
- mBubbleOverflow = new BubbleOverflow(getContext());
- mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
- } else {
- mBubbleContainer.removeView(mBubbleOverflow.getIconView());
- mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
- overflowBtnIndex = mBubbleContainer.getChildCount();
- }
- mBubbleContainer.addView(mBubbleOverflow.getIconView(), overflowBtnIndex,
- new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- mBubbleOverflow.getIconView().setOnClickListener(v -> {
- setSelectedBubble(mBubbleOverflow);
- showManageMenu(false);
- });
+ private void updateOverflow() {
+ mBubbleOverflow.update();
+ mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
+ mBubbleContainer.getChildCount() - 1 /* index */);
updateOverflowVisibility();
}
+
/**
* Handle theme changes.
*/
public void onThemeChanged() {
setUpFlyout();
- setUpOverflow();
setUpUserEducation();
setUpManageMenu();
+ updateOverflow();
updateExpandedViewTheme();
}
@@ -1261,7 +1220,7 @@ public class BubbleStackView extends FrameLayout
/** Respond to the display size change by recalculating view size and location. */
public void onDisplaySizeChanged() {
- setUpOverflow();
+ updateOverflow();
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getRealSize(mDisplaySize);
@@ -1279,12 +1238,7 @@ public class BubbleStackView extends FrameLayout
}
mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
mStackAnimationController.updateResources(mOrientation);
-
- final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
- mDismissTargetCircle.getLayoutParams().width = targetSize;
- mDismissTargetCircle.getLayoutParams().height = targetSize;
- mDismissTargetCircle.requestLayout();
-
+ mDismissView.updateResources();
mMagneticTarget.setMagneticFieldRadiusPx(mBubbleSize * 2);
}
@@ -1796,28 +1750,8 @@ public class BubbleStackView extends FrameLayout
&& mManageEducationView.getVisibility() != VISIBLE
&& mIsExpanded
&& mExpandedBubble.getExpandedView() != null) {
- mManageEducationView.setAlpha(0);
- mManageEducationView.setVisibility(VISIBLE);
- mManageEducationView.post(() -> {
- mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
- final int viewHeight = mManageEducationView.getManageViewHeight();
- final int inset = getResources().getDimensionPixelSize(
- R.dimen.bubbles_manage_education_top_inset);
- mManageEducationView.bringToFront();
- mManageEducationView.setManageViewPosition(0, mTempRect.top - viewHeight + inset);
- mManageEducationView.animate()
- .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
- .setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
- mManageEducationView.findViewById(R.id.manage).setOnClickListener(view -> {
- mExpandedBubble.getExpandedView().findViewById(R.id.settings_button)
- .performClick();
- maybeShowManageEducation(false);
- });
- mManageEducationView.findViewById(R.id.got_it).setOnClickListener(view ->
- maybeShowManageEducation(false));
- mManageEducationView.setOnClickListener(view ->
- maybeShowManageEducation(false));
- });
+ mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect,
+ () -> maybeShowManageEducation(false) /* run on click */);
Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true);
} else if (!show
&& mManageEducationView.getVisibility() == VISIBLE
@@ -2362,48 +2296,6 @@ public class BubbleStackView extends FrameLayout
}
}
- /** Animates in the dismiss target. */
- private void springInDismissTargetMaybe() {
- if (mShowingDismiss) {
- return;
- }
-
- mShowingDismiss = true;
-
- mDismissTargetContainer.bringToFront();
- mDismissTargetContainer.setZ(Short.MAX_VALUE - 1);
- mDismissTargetContainer.setVisibility(VISIBLE);
-
- ((TransitionDrawable) mDismissTargetContainer.getBackground()).startTransition(
- DISMISS_TRANSITION_DURATION_MS);
-
- mDismissTargetAnimator.cancel();
- mDismissTargetAnimator
- .spring(DynamicAnimation.TRANSLATION_Y, 0f, mDismissTargetSpring)
- .start();
- }
-
- /**
- * Animates the dismiss target out, as well as the circle that encircles the bubbles, if they
- * were dragged into the target and encircled.
- */
- private void hideDismissTarget() {
- if (!mShowingDismiss) {
- return;
- }
-
- mShowingDismiss = false;
-
- ((TransitionDrawable) mDismissTargetContainer.getBackground()).reverseTransition(
- DISMISS_TRANSITION_DURATION_MS);
-
- mDismissTargetAnimator
- .spring(DynamicAnimation.TRANSLATION_Y, mDismissTargetContainer.getHeight(),
- mDismissTargetSpring)
- .withEndActions(() -> mDismissTargetContainer.setVisibility(View.INVISIBLE))
- .start();
- }
-
/** Animates the flyout collapsed (to dot), or the reverse, starting with the given velocity. */
private void animateFlyoutCollapsed(boolean collapsed, float velX) {
final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
new file mode 100644
index 000000000000..71faf4a2eeb7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
@@ -0,0 +1,85 @@
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.graphics.drawable.TransitionDrawable
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
+import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
+import com.android.systemui.R
+import com.android.systemui.util.DismissCircleView
+import com.android.systemui.util.animation.PhysicsAnimator
+
+/*
+ * View that handles interactions between DismissCircleView and BubbleStackView.
+ */
+class DismissView(context: Context) : FrameLayout(context) {
+
+ var circle = DismissCircleView(context).apply {
+ val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ val newParams = LayoutParams(targetSize, targetSize)
+ newParams.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ setLayoutParams(newParams)
+ setTranslationY(
+ resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height).toFloat())
+ }
+
+ var isShowing = false
+ private val animator = PhysicsAnimator.getInstance(circle)
+ private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY);
+ private val DISMISS_SCRIM_FADE_MS = 200
+ init {
+ setLayoutParams(LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
+ Gravity.BOTTOM))
+ setPadding(0, 0, 0, resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
+ setClipToPadding(false)
+ setClipChildren(false)
+ setVisibility(View.INVISIBLE)
+ setBackgroundResource(
+ R.drawable.floating_dismiss_gradient_transition)
+ addView(circle)
+ }
+
+ /**
+ * Animates this view in.
+ */
+ fun show() {
+ if (isShowing) return
+ isShowing = true
+ bringToFront()
+ setZ(Short.MAX_VALUE - 1f)
+ setVisibility(View.VISIBLE)
+ (getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS)
+ animator.cancel()
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
+ .start()
+ }
+
+ /**
+ * Animates this view out, as well as the circle that encircles the bubbles, if they
+ * were dragged into the target and encircled.
+ */
+ fun hide() {
+ if (!isShowing) return
+ isShowing = false
+ (getBackground() as TransitionDrawable).reverseTransition(DISMISS_SCRIM_FADE_MS)
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
+ spring)
+ .withEndActions({ setVisibility(View.INVISIBLE) })
+ .start()
+ }
+
+ fun updateResources() {
+ val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ circle.layoutParams.width = targetSize
+ circle.layoutParams.height = targetSize
+ circle.requestLayout()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
new file mode 100644
index 000000000000..c58ab31c4561
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.bubbles
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.View
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+
+/**
+ * Educational view to highlight the manage button that allows a user to configure the settings
+ * for the bubble. Shown only the first time a user expands a bubble.
+ */
+class ManageEducationView @JvmOverloads constructor(
+ context: Context?,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
+ private val manageButton by lazy { findViewById<Button>(R.id.manage) }
+ private val gotItButton by lazy { findViewById<Button>(R.id.got_it) }
+ private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) }
+ private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) }
+ private var isInflated = false
+
+ init {
+ this.visibility = View.GONE
+ this.elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
+ this.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+ }
+
+ override fun setLayoutDirection(direction: Int) {
+ super.setLayoutDirection(direction)
+ // setLayoutDirection runs before onFinishInflate
+ // so skip if views haven't inflated; otherwise we'll get NPEs
+ if (!isInflated) return
+ setDirection()
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ isInflated = true
+ setDirection()
+ setTextColor()
+ }
+
+ private fun setTextColor() {
+ val typedArray = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
+ android.R.attr.textColorPrimaryInverse))
+ val bgColor = typedArray.getColor(0 /* index */, Color.BLACK)
+ var textColor = typedArray.getColor(1 /* index */, Color.WHITE)
+ typedArray.recycle()
+ textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true)
+ titleTextView.setTextColor(textColor)
+ descTextView.setTextColor(textColor)
+ }
+
+ fun setDirection() {
+ manageView.setBackgroundResource(
+ if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL)
+ R.drawable.bubble_stack_user_education_bg_rtl
+ else R.drawable.bubble_stack_user_education_bg)
+ titleTextView.gravity = Gravity.START
+ descTextView.gravity = Gravity.START
+ }
+
+ fun show(expandedView: BubbleExpandedView, rect : Rect, hideMenu: Runnable) {
+ alpha = 0f
+ visibility = View.VISIBLE
+ post {
+ expandedView.getManageButtonBoundsOnScreen(rect)
+ with(hideMenu) {
+ manageButton
+ .setOnClickListener {
+ expandedView.findViewById<View>(R.id.settings_button).performClick()
+ this.run()
+ }
+ gotItButton.setOnClickListener { this.run() }
+ setOnClickListener { this.run() }
+ }
+ with(manageView) {
+ translationX = 0f
+ val inset = resources.getDimensionPixelSize(
+ R.dimen.bubbles_manage_education_top_inset)
+ translationY = (rect.top - manageView.height + inset).toFloat()
+ }
+ bringToFront()
+ animate()
+ .setDuration(BubbleStackView.ANIMATE_STACK_USER_EDUCATION_DURATION.toLong())
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .alpha(1f)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 662831e4a445..24ca9708a4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -27,6 +27,7 @@ import javax.inject.Inject
import javax.inject.Singleton
private const val TAG = "MediaDataFilter"
+private const val DEBUG = true
/**
* Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user
@@ -98,7 +99,7 @@ class MediaDataFilter @Inject constructor(
// are up to date
mediaEntries.clear()
keyCopy.forEach {
- Log.d(TAG, "Removing $it after user change")
+ if (DEBUG) Log.d(TAG, "Removing $it after user change")
listenersCopy.forEach { listener ->
listener.onMediaDataRemoved(it)
}
@@ -106,7 +107,7 @@ class MediaDataFilter @Inject constructor(
dataSource.getData().forEach { (key, data) ->
if (lockscreenUserManager.isCurrentProfile(data.userId)) {
- Log.d(TAG, "Re-adding $key after user change")
+ if (DEBUG) Log.d(TAG, "Re-adding $key after user change")
mediaEntries.put(key, data)
listenersCopy.forEach { listener ->
listener.onMediaDataLoaded(key, null, data)
@@ -119,6 +120,7 @@ class MediaDataFilter @Inject constructor(
* Invoked when the user has dismissed the media carousel
*/
fun onSwipeToDismiss() {
+ if (DEBUG) Log.d(TAG, "Media carousel swiped away")
val mediaKeys = mediaEntries.keys.toSet()
mediaKeys.forEach {
mediaDataManager.setTimedOut(it, timedOut = true)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index b3277737f397..d82150f2346b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -63,6 +63,7 @@ private val ART_URIS = arrayOf(
)
private const val TAG = "MediaDataManager"
+private const val DEBUG = true
private const val DEFAULT_LUMINOSITY = 0.25f
private const val LUMINOSITY_THRESHOLD = 0.05f
private const val SATURATION_MULTIPLIER = 0.8f
@@ -253,7 +254,7 @@ class MediaDataManager(
fun removeListener(listener: Listener) = listeners.remove(listener)
/**
- * Called whenever the player has been paused or stopped for a while.
+ * Called whenever the player has been paused or stopped for a while, or swiped from QQS.
* This will make the player not active anymore, hiding it from QQS and Keyguard.
* @see MediaData.active
*/
@@ -263,6 +264,7 @@ class MediaDataManager(
return
}
it.active = !timedOut
+ if (DEBUG) Log.d(TAG, "Updating $token timedOut: $timedOut")
onMediaDataLoaded(token, token, it)
}
}
@@ -283,7 +285,9 @@ class MediaDataManager(
return
}
- Log.d(TAG, "adding track for $userId from browser: $desc")
+ if (DEBUG) {
+ Log.d(TAG, "adding track for $userId from browser: $desc")
+ }
// Album art
var artworkBitmap = desc.iconBitmap
@@ -383,7 +387,7 @@ class MediaDataManager(
if (actions != null) {
for ((index, action) in actions.withIndex()) {
if (action.getIcon() == null) {
- Log.i(TAG, "No icon for action $index ${action.title}")
+ if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}")
actionsToShowCollapsed.remove(index)
continue
}
@@ -427,7 +431,7 @@ class MediaDataManager(
if (!TextUtils.isEmpty(uriString)) {
val albumArt = loadBitmapFromUri(Uri.parse(uriString))
if (albumArt != null) {
- Log.d(TAG, "loaded art from $uri")
+ if (DEBUG) Log.d(TAG, "loaded art from $uri")
return albumArt
}
}
@@ -514,7 +518,7 @@ class MediaDataManager(
Assert.isMainThread()
val removed = mediaEntries.remove(key)
if (useMediaResumption && removed?.resumeAction != null) {
- Log.d(TAG, "Not removing $key because resumable")
+ if (DEBUG) Log.d(TAG, "Not removing $key because resumable")
// Move to resume key (aka package name) if that key doesn't already exist.
val resumeAction = getResumeMediaAction(removed.resumeAction!!)
val updated = removed.copy(token = null, actions = listOf(resumeAction),
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9115b4849355..d03082e6b442 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -809,7 +809,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void addCallback(OverviewProxyListener listener) {
- mConnectionCallbacks.add(listener);
+ if (!mConnectionCallbacks.contains(listener)) {
+ mConnectionCallbacks.add(listener);
+ }
listener.onConnectionChanged(mOverviewProxy != null);
listener.onNavBarButtonAlphaChanged(mNavBarButtonAlpha, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 8c24c540e743..2638d28733e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -154,7 +154,7 @@ public class NavigationBarController implements Callbacks {
Dependency.get(IWindowManager.class));
navBar.setAutoHideController(autoHideController);
navBar.restoreAppearanceAndTransientState();
- mNavigationBars.append(displayId, navBar);
+ mNavigationBars.put(displayId, navBar);
if (result != null) {
navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index a35aca553c4f..9606318e1992 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -121,6 +121,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
private final Context mContext;
private final OverviewProxyService mOverviewProxyService;
+ private final SysUiState mSysUiState;
private final Runnable mStateChangeCallback;
private final PluginManager mPluginManager;
@@ -197,14 +198,22 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
}
};
+ private final SysUiState.SysUiStateCallback mSysUiStateCallback =
+ new SysUiState.SysUiStateCallback() {
+ @Override
+ public void onSystemUiStateChanged(int sysUiFlags) {
+ mSysUiFlags = sysUiFlags;
+ }
+ };
+
public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
- SysUiState sysUiFlagContainer, PluginManager pluginManager,
- Runnable stateChangeCallback) {
+ SysUiState sysUiState, PluginManager pluginManager, Runnable stateChangeCallback) {
super(Dependency.get(BroadcastDispatcher.class));
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = context.getMainExecutor();
mOverviewProxyService = overviewProxyService;
+ mSysUiState = sysUiState;
mPluginManager = pluginManager;
mStateChangeCallback = stateChangeCallback;
ComponentName recentsComponentName = ComponentName.unflattenFromString(
@@ -238,7 +247,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged);
updateCurrentUserResources();
- sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
}
public void updateCurrentUserResources() {
@@ -287,6 +295,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mIsAttached = true;
Dependency.get(ProtoTracer.class).add(this);
mOverviewProxyService.addCallback(mQuickSwitchListener);
+ mSysUiState.addCallback(mSysUiStateCallback);
updateIsEnabled();
startTracking();
}
@@ -298,6 +307,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mIsAttached = false;
Dependency.get(ProtoTracer.class).remove(this);
mOverviewProxyService.removeCallback(mQuickSwitchListener);
+ mSysUiState.removeCallback(mSysUiStateCallback);
updateIsEnabled();
stopTracking();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index b7733cc5acd7..f43fa648a1a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -120,6 +120,7 @@ import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -131,6 +132,7 @@ import com.android.systemui.util.LifecycleFragment;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
@@ -354,6 +356,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
// If the button will actually become visible and the navbar is about to hide,
// tell the statusbar to keep it around for longer
mAutoHideController.touchAutoHide();
+ mNavigationBarView.notifyActiveTouchRegions();
}
};
@@ -550,6 +553,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
mOrientationHandleGlobalLayoutListener);
}
+ mHandler.removeCallbacks(mAutoDim);
+ mNavigationBarView = null;
+ mOrientationHandle = null;
}
@Override
@@ -1458,11 +1464,11 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
if (navigationBarView == null) return null;
- final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
- .create(NavigationBarFragment.class);
navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
+ final NavigationBarFragment fragment =
+ FragmentHostManager.get(v).create(NavigationBarFragment.class);
final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
fragmentHost.getFragmentManager().beginTransaction()
.replace(R.id.navigation_bar_frame, fragment, TAG)
@@ -1472,6 +1478,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
@Override
public void onViewDetachedFromWindow(View v) {
+ final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
+ fragmentHost.removeTagListener(TAG, listener);
FragmentHostManager.removeAndDestroy(v);
navigationBarView.removeOnAttachStateChangeListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 7936e533f76d..84512ac85fa9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -115,12 +115,8 @@ public class NavigationBarView extends FrameLayout implements
int mNavigationIconHints = 0;
private int mNavBarMode;
- private Rect mHomeButtonBounds = new Rect();
- private Rect mBackButtonBounds = new Rect();
- private Rect mRecentsButtonBounds = new Rect();
- private Rect mRotationButtonBounds = new Rect();
private final Region mActiveRegion = new Region();
- private int[] mTmpPosition = new int[2];
+ private Rect mTmpBounds = new Rect();
private KeyButtonDrawable mBackIcon;
private KeyButtonDrawable mHomeDefaultIcon;
@@ -712,6 +708,7 @@ public class NavigationBarView extends FrameLayout implements
getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
+ notifyActiveTouchRegions();
}
@VisibleForTesting
@@ -929,42 +926,30 @@ public class NavigationBarView extends FrameLayout implements
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
+ notifyActiveTouchRegions();
+ mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
+ }
+
+ /**
+ * Notifies the overview service of the active touch regions.
+ */
+ public void notifyActiveTouchRegions() {
mActiveRegion.setEmpty();
- updateButtonLocation(getBackButton(), mBackButtonBounds, true);
- updateButtonLocation(getHomeButton(), mHomeButtonBounds, false);
- updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false);
- updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true);
- // TODO: Handle button visibility changes
+ updateButtonLocation(getBackButton());
+ updateButtonLocation(getHomeButton());
+ updateButtonLocation(getRecentsButton());
+ updateButtonLocation(getRotateSuggestionButton());
mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
- mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
}
- private void updateButtonLocation(ButtonDispatcher button, Rect buttonBounds,
- boolean isActive) {
+ private void updateButtonLocation(ButtonDispatcher button) {
View view = button.getCurrentView();
- if (view == null) {
- buttonBounds.setEmpty();
+ if (view == null || !button.isVisible()) {
return;
}
- // Temporarily reset the translation back to origin to get the position in window
- final float posX = view.getTranslationX();
- final float posY = view.getTranslationY();
- view.setTranslationX(0);
- view.setTranslationY(0);
-
- if (isActive) {
- view.getLocationOnScreen(mTmpPosition);
- buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
- mTmpPosition[0] + view.getMeasuredWidth(),
- mTmpPosition[1] + view.getMeasuredHeight());
- mActiveRegion.op(buttonBounds, Op.UNION);
- }
- view.getLocationInWindow(mTmpPosition);
- buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
- mTmpPosition[0] + view.getMeasuredWidth(),
- mTmpPosition[1] + view.getMeasuredHeight());
- view.setTranslationX(posX);
- view.setTranslationY(posY);
+
+ view.getBoundsOnScreen(mTmpBounds);
+ mActiveRegion.op(mTmpBounds, Op.UNION);
}
private void updateOrientationViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1f2a2c652331..c5571e8ceb20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -18,9 +18,6 @@ package com.android.systemui.statusbar.phone;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
-import static android.app.StatusBarManager.DISABLE_CLOCK;
-import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
@@ -4174,7 +4171,6 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
mTopHidesStatusBar = topAppHidesStatusBar;
- updateStatusBarIcons(topAppHidesStatusBar);
if (!topAppHidesStatusBar && mWereIconsJustHidden) {
// Immediately update the icon hidden state, since that should only apply if we're
// staying fullscreen.
@@ -4184,17 +4180,6 @@ public class StatusBar extends SystemUI implements DemoMode,
updateHideIconsForBouncer(true /* animate */);
}
- private void updateStatusBarIcons(boolean topAppHidesStatusBar) {
- int flags1 = StatusBarManager.DISABLE_NONE;
- int flags2 = StatusBarManager.DISABLE2_NONE;
- if (topAppHidesStatusBar) {
- flags1 = DISABLE_NOTIFICATION_ICONS | DISABLE_CLOCK;
- flags2 = DISABLE2_SYSTEM_ICONS;
- }
-
- mCommandQueue.disable(mDisplayId, flags1, flags2, false);
- }
-
protected void toggleKeyboardShortcuts(int deviceId) {
KeyboardShortcuts.toggle(mContext, deviceId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 7561af770298..b1241b160d70 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -70,6 +70,8 @@ public class UsbDebuggingActivity extends AlertActivity
if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+ IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+ mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
}
Intent intent = getIntent();
@@ -119,6 +121,7 @@ public class UsbDebuggingActivity extends AlertActivity
}
boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
if (!connected) {
+ Log.d(TAG, "USB disconnected, notifying service");
notifyService(false);
mActivity.finish();
}
@@ -126,29 +129,20 @@ public class UsbDebuggingActivity extends AlertActivity
}
@Override
- public void onStart() {
- super.onStart();
- if (mDisconnectedReceiver != null) {
- IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
- mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
- }
- }
-
- @Override
- protected void onStop() {
+ protected void onDestroy() {
if (mDisconnectedReceiver != null) {
mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
}
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- // If the ADB service has not yet been notified due to this dialog being closed in some
- // other way then notify the service to deny the connection to ensure system_server sends
- // a response to adbd.
- if (!mServiceNotified) {
- notifyService(false);
+ // Only notify the service if the activity is finishing; if onDestroy has been called due to
+ // a configuration change then allow the user to still authorize the connection the next
+ // time the activity is in the foreground.
+ if (isFinishing()) {
+ // If the ADB service has not yet been notified due to this dialog being closed in some
+ // other way then notify the service to deny the connection to ensure system_server
+ // sends a response to adbd.
+ if (!mServiceNotified) {
+ notifyService(false);
+ }
}
super.onDestroy();
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
index bba5ff5b54ca..fbc167683a2a 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
@@ -52,16 +52,16 @@ public class WindowManagerShellModule {
@Singleton
@Provides
- static SystemWindows provideSystemWindows(Context context, DisplayController displayController,
+ static SystemWindows provideSystemWindows(DisplayController displayController,
IWindowManager wmService) {
- return new SystemWindows(context, displayController, wmService);
+ return new SystemWindows(displayController, wmService);
}
@Singleton
@Provides
static DisplayImeController provideDisplayImeController(
- SystemWindows syswin, DisplayController displayController,
+ IWindowManager wmService, DisplayController displayController,
@Main Handler mainHandler, TransactionPool transactionPool) {
- return new DisplayImeController(syswin, displayController, mainHandler, transactionPool);
+ return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
new file mode 100644
index 000000000000..9cb4fb319fa2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.hardware.display.DisplayManager;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DisplayIdIndexSupplierTest extends SysuiTestCase {
+
+ private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier;
+
+ @Before
+ public void setUp() throws Exception {
+ mDisplayIdIndexSupplier = new DisplayIdIndexSupplier(
+ mContext.getSystemService(DisplayManager.class)) {
+
+ @NonNull
+ @Override
+ protected Object createInstance(Display display) {
+ return new Object();
+ }
+ };
+ }
+
+ @Test
+ public void get_instanceIsNotNull() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ assertNotNull(object);
+ }
+
+ @Test
+ public void get_removeExistedObject_newObject() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ mDisplayIdIndexSupplier.remove(Display.DEFAULT_DISPLAY);
+
+ Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+ assertNotEquals(object, newObject);
+ }
+
+ @Test
+ public void get_clearAllObjects_newObject() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ mDisplayIdIndexSupplier.clear();
+
+ Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+ assertNotEquals(object, newObject);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index d6d2fcd9610a..69482791ef23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -16,20 +16,13 @@
package com.android.systemui.accessibility;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.view.Display;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
import androidx.test.filters.SmallTest;
@@ -38,45 +31,43 @@ import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
/** Tests the ModeSwitchesController. */
public class ModeSwitchesControllerTest extends SysuiTestCase {
- private WindowManager mWindowManager;
+ @Mock
+ private ModeSwitchesController.SwitchSupplier mSupplier;
+ @Mock
+ private MagnificationModeSwitch mModeSwitch;
private ModeSwitchesController mModeSwitchesController;
+
@Before
public void setUp() {
- mWindowManager = mock(WindowManager.class);
- Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
- when(mWindowManager.getDefaultDisplay()).thenReturn(display);
- WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
- .getMaximumWindowMetrics();
- when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
- mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
- mModeSwitchesController = new ModeSwitchesController(mContext);
+ MockitoAnnotations.initMocks(this);
+ when(mSupplier.get(anyInt())).thenReturn(mModeSwitch);
+ mModeSwitchesController = new ModeSwitchesController(mSupplier);
}
@Test
public void testShowButton() {
mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- verify(mWindowManager).addView(any(), any());
+
+ verify(mModeSwitch).showButton(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
}
@Test
public void testRemoveButton() {
mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
- verify(mWindowManager).addView(captor.capture(), any(WindowManager.LayoutParams.class));
mModeSwitchesController.removeButton(Display.DEFAULT_DISPLAY);
- verify(mWindowManager).removeView(eq(captor.getValue()));
+ verify(mModeSwitch).removeButton();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index e8a0c738f966..d4a94c5b9e66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -45,6 +45,7 @@ import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -530,6 +531,11 @@ public class AuthControllerTest extends SysuiTestCase {
IActivityTaskManager getActivityTaskManager() {
return mock(IActivityTaskManager.class);
}
+
+ @Override
+ FingerprintManager getFingerprintManager(Context context) {
+ return mock(FingerprintManager.class);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b6ad1a526165..1038069f5d11 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2050,7 +2050,9 @@ public final class ProcessList {
final int pid = precedence.pid;
long now = System.currentTimeMillis();
final long end = now + PROC_KILL_TIMEOUT;
+ final int oldPolicy = StrictMode.getThreadPolicyMask();
try {
+ StrictMode.setThreadPolicyMask(0);
Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
// It's killed successfully, but we'd make sure the cleanup work is done.
synchronized (precedence) {
@@ -2069,9 +2071,11 @@ public final class ProcessList {
}
}
} catch (Exception e) {
- // It's still alive...
+ // It's still alive... maybe blocked at uninterruptible sleep ?
Slog.wtf(TAG, precedence.toString() + " refused to die, but we need to launch "
- + app);
+ + app, e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldPolicy);
}
}
try {
@@ -2416,7 +2420,15 @@ public final class ProcessList {
ProcessList.killProcessGroup(app.uid, app.pid);
checkSlow(startTime, "startProcess: done killing old proc");
- Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ if (!app.killed || mService.mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL
+ || app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
+ || app.lastCachedPss < getCachedRestoreThresholdKb()) {
+ // Throw a wtf if it's not killed, or killed but not because the system was in
+ // memory pressure + the app was in "cch-empty" and used large amount of memory
+ Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ } else {
+ Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ }
// We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
// routine of it yet, but we'd set it as the precedence of the new process.
precedence = app;
@@ -2819,7 +2831,15 @@ public final class ProcessList {
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
} else if (old != null) {
- Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ if (old.killed) {
+ // The old process has been killed, we probably haven't had
+ // a chance to clean up the old record, just log a warning
+ Slog.w(TAG, "Existing proc " + old + " was killed "
+ + (SystemClock.uptimeMillis() - old.mKillTime)
+ + "ms ago when adding " + proc);
+ } else {
+ Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ }
}
UidRecord uidRec = mActiveUids.get(proc.uid);
if (uidRec == null) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 42e3061a840e..6e1bd8faeaf9 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -352,6 +352,8 @@ class ProcessRecord implements WindowProcessListener {
boolean mReachable; // Whether or not this process is reachable from given process
+ long mKillTime; // The timestamp in uptime when this process was killed.
+
void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
long startTime) {
this.startUid = startUid;
@@ -925,6 +927,7 @@ class ProcessRecord implements WindowProcessListener {
if (!mPersistent) {
killed = true;
killedByAm = true;
+ mKillTime = SystemClock.uptimeMillis();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 3ff6ec1afa41..86e6a3220507 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -244,7 +244,7 @@ abstract class HdmiCecLocalDevice {
if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
return false;
}
- // Cache incoming message. Note that it caches only white-listed one.
+ // Cache incoming message if it is included in the list of cacheable opcodes.
mCecMessageCache.cacheMessage(message);
return onMessage(message);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index aed94fc85431..64d70d6601f6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -225,7 +225,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
if (SystemProperties.getBoolean(Constants.PROPERTY_KEEP_AWAKE, true)) {
mWakeLock = new SystemWakeLock();
} else {
- // Create a dummy lock object that doesn't do anything about wake lock,
+ // Create a stub lock object that doesn't do anything about wake lock,
// hence allows the device to go to sleep even if it's the active source.
mWakeLock = new ActiveWakeLock() {
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 2c0ddaf35182..804cc92cca08 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1667,6 +1667,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (avr == null) {
return;
}
+ setArcStatus(false);
// Seq #44.
removeAction(RequestArcInitiationAction.class);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0154fe07a418..254285dfbd41 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2942,7 +2942,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
vis = 0;
}
if (!mCurPerceptible) {
- vis = 0;
+ vis &= ~InputMethodService.IME_VISIBLE;
}
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index f69c8239762d..d933c109b27d 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,6 +17,9 @@
package com.android.server.location;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_MOCK_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -120,10 +123,16 @@ import com.android.server.location.util.AppForegroundHelper;
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationAttributionHelper;
+import com.android.server.location.util.LocationPermissionsHelper;
+import com.android.server.location.util.LocationPowerSaveModeHelper;
import com.android.server.location.util.LocationUsageLogger;
+import com.android.server.location.util.ScreenInteractiveHelper;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.SystemAppForegroundHelper;
import com.android.server.location.util.SystemAppOpsHelper;
+import com.android.server.location.util.SystemLocationPermissionsHelper;
+import com.android.server.location.util.SystemLocationPowerSaveModeHelper;
+import com.android.server.location.util.SystemScreenInteractiveHelper;
import com.android.server.location.util.SystemSettingsHelper;
import com.android.server.location.util.SystemUserInfoHelper;
import com.android.server.location.util.UserInfoHelper;
@@ -173,7 +182,7 @@ public class LocationManagerService extends ILocationManager.Stub {
publishBinderService(Context.LOCATION_SERVICE, mService);
// client caching behavior is only enabled after seeing the first invalidate
- invalidateLocalLocationEnabledCaches();
+ LocationManager.invalidateLocalLocationEnabledCaches();
// disable caching for our own process
Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
.disableLocalLocationEnabledCaches();
@@ -486,7 +495,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private void onLocationModeChanged(int userId) {
boolean enabled = mSettingsHelper.isLocationEnabled(userId);
- invalidateLocalLocationEnabledCaches();
+ LocationManager.invalidateLocalLocationEnabledCaches();
if (D) {
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
@@ -1232,19 +1241,20 @@ public class LocationManagerService extends ILocationManager.Stub {
if (!currentlyMonitoring) {
if (allowMonitoring) {
if (!highPower) {
- return mAppOpsHelper.startLocationMonitoring(mCallerIdentity);
+ return mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, mCallerIdentity);
} else {
- return mAppOpsHelper.startHighPowerLocationMonitoring(mCallerIdentity);
+ return mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ mCallerIdentity);
}
}
} else {
- if (!allowMonitoring || !mAppOpsHelper.checkLocationAccess(mCallerIdentity,
+ if (!allowMonitoring || !mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
LocationPermissions.getPermissionLevel(mContext, mCallerIdentity.getUid(),
- mCallerIdentity.getPid()))) {
+ mCallerIdentity.getPid())), mCallerIdentity)) {
if (!highPower) {
- mAppOpsHelper.stopLocationMonitoring(mCallerIdentity);
+ mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, mCallerIdentity);
} else {
- mAppOpsHelper.stopHighPowerLocationMonitoring(mCallerIdentity);
+ mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, mCallerIdentity);
}
return false;
}
@@ -1589,8 +1599,9 @@ public class LocationManagerService extends ILocationManager.Stub {
continue;
}
- if (!mAppOpsHelper.checkLocationAccess(identity,
- record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE)) {
+ if (!mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
+ record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE),
+ identity)) {
continue;
}
final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
@@ -2118,7 +2129,8 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// appops check should always be right before delivery
- if (!mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+ if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ identity)) {
return null;
}
@@ -2179,7 +2191,8 @@ public class LocationManagerService extends ILocationManager.Stub {
if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
// appops check should always be right before delivery
- if (mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+ if (mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ identity)) {
transport.deliverResult(lastLocation);
} else {
transport.deliverResult(null);
@@ -2329,7 +2342,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+ public void sendExtraCommand(String provider, String command, Bundle extras) {
LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_COARSE);
mContext.enforceCallingOrSelfPermission(
permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
@@ -2350,8 +2363,6 @@ public class LocationManagerService extends ILocationManager.Stub {
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_SEND_EXTRA_COMMAND,
provider);
-
- return true;
}
@Override
@@ -2553,7 +2564,8 @@ public class LocationManagerService extends ILocationManager.Stub {
r.mLastFixBroadcast = location;
// appops check should always be right before delivery
- if (!mAppOpsHelper.noteLocationAccess(receiver.mCallerIdentity, permissionLevel)) {
+ if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ receiver.mCallerIdentity)) {
continue;
}
@@ -2644,7 +2656,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2664,7 +2676,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2687,7 +2699,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2708,7 +2720,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2926,24 +2938,36 @@ public class LocationManagerService extends ILocationManager.Stub {
private final UserInfoHelper mUserInfoHelper;
private final SystemAppOpsHelper mAppOpsHelper;
+ private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
private final SystemSettingsHelper mSettingsHelper;
private final SystemAppForegroundHelper mAppForegroundHelper;
- private final LocationUsageLogger mLocationUsageLogger;
+ private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+ private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
+ private final LocationUsageLogger mLocationUsageLogger;
+ private final LocationRequestStatistics mLocationRequestStatistics;
SystemInjector(Context context, UserInfoHelper userInfoHelper) {
mUserInfoHelper = userInfoHelper;
mAppOpsHelper = new SystemAppOpsHelper(context);
+ mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
+ mAppOpsHelper);
mSettingsHelper = new SystemSettingsHelper(context);
mAppForegroundHelper = new SystemAppForegroundHelper(context);
- mLocationUsageLogger = new LocationUsageLogger();
+ mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context);
+ mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+ mLocationUsageLogger = new LocationUsageLogger();
+ mLocationRequestStatistics = new LocationRequestStatistics();
}
void onSystemReady() {
mAppOpsHelper.onSystemReady();
+ mLocationPermissionsHelper.onSystemReady();
mSettingsHelper.onSystemReady();
mAppForegroundHelper.onSystemReady();
+ mLocationPowerSaveModeHelper.onSystemReady();
+ mScreenInteractiveHelper.onSystemReady();
}
@Override
@@ -2957,6 +2981,11 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
+ public LocationPermissionsHelper getLocationPermissionsHelper() {
+ return mLocationPermissionsHelper;
+ }
+
+ @Override
public SettingsHelper getSettingsHelper() {
return mSettingsHelper;
}
@@ -2972,8 +3001,23 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
+ public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+ return mLocationPowerSaveModeHelper;
+ }
+
+ @Override
+ public ScreenInteractiveHelper getScreenInteractiveHelper() {
+ return mScreenInteractiveHelper;
+ }
+
+ @Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
+
+ @Override
+ public LocationRequestStatistics getLocationRequestStatistics() {
+ return mLocationRequestStatistics;
+ }
}
}
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index f37992a456ac..f6896b86f9b9 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -50,14 +50,10 @@ public class PassiveProvider extends AbstractLocationProvider {
Criteria.POWER_LOW,
Criteria.ACCURACY_COARSE);
- private volatile boolean mReportLocation;
-
public PassiveProvider(Context context) {
// using a direct executor is ok because this class has no locks that could deadlock
super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context));
- mReportLocation = false;
-
setProperties(PROPERTIES);
setAllowed(true);
}
@@ -66,15 +62,11 @@ public class PassiveProvider extends AbstractLocationProvider {
* Pass a location into the passive provider.
*/
public void updateLocation(Location location) {
- if (mReportLocation) {
- reportLocation(location);
- }
+ reportLocation(location);
}
@Override
- public void onSetRequest(ProviderRequest request) {
- mReportLocation = request.reportLocation;
- }
+ public void onSetRequest(ProviderRequest request) {}
@Override
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index c855a12606b2..2d9734ef0553 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -16,7 +16,6 @@
package com.android.server.location.geofence;
-import static android.Manifest.permission;
import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
@@ -44,8 +43,8 @@ import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.PendingIntentListenerRegistration;
-import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
import com.android.server.location.util.LocationUsageLogger;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.UserInfoHelper;
@@ -86,7 +85,7 @@ public class GeofenceManager extends
// we store these values because we don't trust the listeners not to give us dupes, not to
// spam us, and because checking the values may be more expensive
- private boolean mAppOpsAllowed;
+ private boolean mPermitted;
private @Nullable Location mCachedLocation;
private float mCachedLocationDistanceM;
@@ -101,7 +100,7 @@ public class GeofenceManager extends
mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- TAG + ":" + identity.getPackageName());
+ TAG + ":" + identity.getPackageName());
mWakeLock.setReferenceCounted(true);
mWakeLock.setWorkSource(identity.addToWorkSource(null));
}
@@ -114,7 +113,8 @@ public class GeofenceManager extends
@Override
protected void onPendingIntentListenerRegister() {
mGeofenceState = STATE_UNKNOWN;
- mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+ mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
}
@Override
@@ -127,18 +127,32 @@ public class GeofenceManager extends
}
}
- boolean isAppOpsAllowed() {
- return mAppOpsAllowed;
+ boolean isPermitted() {
+ return mPermitted;
}
- boolean onAppOpsChanged(String packageName) {
+ boolean onLocationPermissionsChanged(String packageName) {
if (getIdentity().getPackageName().equals(packageName)) {
- boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
- PERMISSION_FINE);
- if (appOpsAllowed != mAppOpsAllowed) {
- mAppOpsAllowed = appOpsAllowed;
- return true;
- }
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ boolean onLocationPermissionsChanged(int uid) {
+ if (getIdentity().getUid() == uid) {
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ private boolean onLocationPermissionsChanged() {
+ boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
+ if (permitted != mPermitted) {
+ mPermitted = permitted;
+ return true;
}
return false;
@@ -186,10 +200,10 @@ public class GeofenceManager extends
mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
try {
- pendingIntent.send(mContext, 0, intent,
- (pI, i, rC, rD, rE) -> mWakeLock.release(),
- null, permission.ACCESS_FINE_LOCATION,
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+ // send() only enforces permissions for broadcast intents, but since clients can
+ // select any kind of pending intent we do not rely on send() to enforce permissions
+ pendingIntent.send(mContext, 0, intent, (pI, i, rC, rD, rE) -> mWakeLock.release(),
+ null, null, PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
} catch (PendingIntent.CanceledException e) {
mWakeLock.release();
removeRegistration(new GeofenceKey(pendingIntent, getRequest()), this);
@@ -202,7 +216,7 @@ public class GeofenceManager extends
builder.append(getIdentity());
ArraySet<String> flags = new ArraySet<>(1);
- if (!mAppOpsAllowed) {
+ if (!mPermitted) {
flags.add("na");
}
if (!flags.isEmpty()) {
@@ -224,10 +238,22 @@ public class GeofenceManager extends
private final SettingsHelper.UserSettingChangedListener
mLocationPackageBlacklistChangedListener =
this::onLocationPackageBlacklistChanged;
- private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+ private final LocationPermissionsHelper.LocationPermissionsListener
+ mLocationPermissionsListener =
+ new LocationPermissionsHelper.LocationPermissionsListener() {
+ @Override
+ public void onLocationPermissionsChanged(String packageName) {
+ GeofenceManager.this.onLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ public void onLocationPermissionsChanged(int uid) {
+ GeofenceManager.this.onLocationPermissionsChanged(uid);
+ }
+ };
protected final UserInfoHelper mUserInfoHelper;
- protected final AppOpsHelper mAppOpsHelper;
+ protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final SettingsHelper mSettingsHelper;
protected final LocationUsageLogger mLocationUsageLogger;
@@ -241,7 +267,7 @@ public class GeofenceManager extends
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
- mAppOpsHelper = injector.getAppOpsHelper();
+ mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
}
@@ -281,7 +307,7 @@ public class GeofenceManager extends
@Override
protected boolean isActive(GeofenceRegistration registration) {
CallerIdentity identity = registration.getIdentity();
- return registration.isAppOpsAllowed()
+ return registration.isPermitted()
&& mUserInfoHelper.isCurrentUserId(identity.getUserId())
&& mSettingsHelper.isLocationEnabled(identity.getUserId())
&& !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
@@ -294,7 +320,7 @@ public class GeofenceManager extends
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.addListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
}
@Override
@@ -303,7 +329,7 @@ public class GeofenceManager extends
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.removeListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
}
@Override
@@ -434,7 +460,11 @@ public class GeofenceManager extends
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onAppOpsChanged(String packageName) {
- updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+ private void onLocationPermissionsChanged(String packageName) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+ }
+
+ private void onLocationPermissionsChanged(int uid) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 53e660ad6475..0b7968be484b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -31,8 +31,8 @@ import com.android.server.LocalServices;
import com.android.server.location.listeners.BinderListenerRegistration;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.util.AppForegroundHelper;
-import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.UserInfoHelper;
import com.android.server.location.util.UserInfoHelper.UserListener;
@@ -65,7 +65,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
// we store these values because we don't trust the listeners not to give us dupes, not to
// spam us, and because checking the values may be more expensive
private boolean mForeground;
- private boolean mAppOpsAllowed;
+ private boolean mPermitted;
protected GnssListenerRegistration(@Nullable TRequest request,
CallerIdentity callerIdentity, TListener listener) {
@@ -84,24 +84,39 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
return mForeground;
}
- boolean isAppOpsAllowed() {
- return mAppOpsAllowed;
+ boolean isPermitted() {
+ return mPermitted;
}
@Override
protected void onBinderListenerRegister() {
- mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+ mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
}
- boolean onAppOpsChanged(String packageName) {
+ boolean onLocationPermissionsChanged(String packageName) {
if (getIdentity().getPackageName().equals(packageName)) {
- boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
- PERMISSION_FINE);
- if (appOpsAllowed != mAppOpsAllowed) {
- mAppOpsAllowed = appOpsAllowed;
- return true;
- }
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ boolean onLocationPermissionsChanged(int uid) {
+ if (getIdentity().getUid() == uid) {
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ private boolean onLocationPermissionsChanged() {
+ boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
+ if (permitted != mPermitted) {
+ mPermitted = permitted;
+ return true;
}
return false;
@@ -125,7 +140,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
if (!mForeground) {
flags.add("bg");
}
- if (!mAppOpsAllowed) {
+ if (!mPermitted) {
flags.add("na");
}
if (!flags.isEmpty()) {
@@ -141,7 +156,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
protected final UserInfoHelper mUserInfoHelper;
protected final SettingsHelper mSettingsHelper;
- protected final AppOpsHelper mAppOpsHelper;
+ protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final AppForegroundHelper mAppForegroundHelper;
protected final LocationManagerInternal mLocationManagerInternal;
@@ -154,14 +169,26 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
private final SettingsHelper.UserSettingChangedListener
mLocationPackageBlacklistChangedListener =
this::onLocationPackageBlacklistChanged;
- private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+ private final LocationPermissionsHelper.LocationPermissionsListener
+ mLocationPermissionsListener =
+ new LocationPermissionsHelper.LocationPermissionsListener() {
+ @Override
+ public void onLocationPermissionsChanged(String packageName) {
+ GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ public void onLocationPermissionsChanged(int uid) {
+ GnssListenerMultiplexer.this.onLocationPermissionsChanged(uid);
+ }
+ };
private final AppForegroundHelper.AppForegroundListener mAppForegroundChangedListener =
this::onAppForegroundChanged;
protected GnssListenerMultiplexer(Injector injector) {
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
- mAppOpsHelper = injector.getAppOpsHelper();
+ mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mAppForegroundHelper = injector.getAppForegroundHelper();
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
@@ -208,7 +235,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
CallerIdentity identity = registration.getIdentity();
// TODO: this should be checking if the gps provider is enabled, not if location is enabled,
// but this is the same for now.
- return registration.isAppOpsAllowed()
+ return registration.isPermitted()
&& (registration.isForeground() || isBackgroundRestrictionExempt(identity))
&& mUserInfoHelper.isCurrentUserId(identity.getUserId())
&& mSettingsHelper.isLocationEnabled(identity.getUserId())
@@ -241,7 +268,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
mBackgroundThrottlePackageWhitelistChangedListener);
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.addListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
mAppForegroundHelper.addListener(mAppForegroundChangedListener);
}
@@ -257,7 +284,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
mBackgroundThrottlePackageWhitelistChangedListener);
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.removeListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
}
@@ -279,8 +306,12 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onAppOpsChanged(String packageName) {
- updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+ private void onLocationPermissionsChanged(String packageName) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+ }
+
+ private void onLocationPermissionsChanged(int uid) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
private void onAppForegroundChanged(int uid, boolean foreground) {
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 8aaf4bf6d8b0..8004ec70aaf3 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -2008,9 +2008,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private final class FusedLocationListener extends LocationChangeListener {
@Override
public void onLocationChanged(Location location) {
- if (LocationManager.FUSED_PROVIDER.equals(location.getProvider())) {
- injectBestLocation(location);
- }
+ injectBestLocation(location);
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 58e725ca152d..8e81f29550d6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -18,10 +18,9 @@ package com.android.server.location.gnss;
import static android.location.LocationManager.GPS_PROVIDER;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
import android.Manifest;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.Context;
import android.location.GnssAntennaInfo;
import android.location.GnssMeasurementCorrections;
@@ -47,10 +46,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
-import com.android.server.location.util.AppForegroundHelper;
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
-import com.android.server.location.util.SettingsHelper;
import java.io.FileDescriptor;
import java.util.List;
@@ -68,9 +65,7 @@ public class GnssManagerService implements GnssNative.Callbacks {
}
private final Context mContext;
- private final SettingsHelper mSettingsHelper;
private final AppOpsHelper mAppOpsHelper;
- private final AppForegroundHelper mAppForegroundHelper;
private final LocationManagerInternal mLocationManagerInternal;
private final GnssLocationProvider mGnssLocationProvider;
@@ -109,9 +104,7 @@ public class GnssManagerService implements GnssNative.Callbacks {
GnssNative.initialize();
mContext = context.createAttributionContext(ATTRIBUTION_ID);
- mSettingsHelper = injector.getSettingsHelper();
mAppOpsHelper = injector.getAppOpsHelper();
- mAppForegroundHelper = injector.getAppForegroundHelper();
mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
if (gnssLocationProvider == null) {
@@ -192,9 +185,10 @@ public class GnssManagerService implements GnssNative.Callbacks {
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName,
String attributionTag) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
- if (!mAppOpsHelper.checkLocationAccess(identity, PERMISSION_FINE)) {
+ if (!mAppOpsHelper.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity)) {
return false;
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 9227a177f861..0815d46a735d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssMeasurementsEvent;
import android.location.GnssRequest;
import android.location.IGnssMeasurementsListener;
@@ -30,6 +30,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationUsageLogger;
import com.android.server.location.util.SettingsHelper;
@@ -47,6 +48,7 @@ public class GnssMeasurementsProvider extends
GnssListenerMultiplexer<GnssRequest, IGnssMeasurementsListener, Boolean> implements
SettingsHelper.GlobalSettingChangedListener {
+ private final AppOpsHelper mAppOpsHelper;
private final LocationUsageLogger mLogger;
private final GnssMeasurementProviderNative mNative;
@@ -57,6 +59,7 @@ public class GnssMeasurementsProvider extends
@VisibleForTesting
public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mLogger = injector.getLocationUsageLogger();
mNative = aNative;
}
@@ -163,7 +166,8 @@ public class GnssMeasurementsProvider extends
*/
public void onMeasurementsAvailable(GnssMeasurementsEvent event) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onGnssMeasurementsReceived(event);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index a07fbe41c975..7dcffc664f52 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssNavigationMessage;
import android.location.IGnssNavigationMessageListener;
import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
/**
@@ -39,6 +40,7 @@ import com.android.server.location.util.Injector;
public class GnssNavigationMessageProvider extends
GnssListenerMultiplexer<Void, IGnssNavigationMessageListener, Void> {
+ private final AppOpsHelper mAppOpsHelper;
private final GnssNavigationMessageProviderNative mNative;
public GnssNavigationMessageProvider(Injector injector) {
@@ -49,6 +51,7 @@ public class GnssNavigationMessageProvider extends
public GnssNavigationMessageProvider(Injector injector,
GnssNavigationMessageProviderNative aNative) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mNative = aNative;
}
@@ -90,7 +93,8 @@ public class GnssNavigationMessageProvider extends
*/
public void onNavigationMessageAvailable(GnssNavigationMessage event) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onGnssNavigationMessageReceived(event);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index d33b05866877..19f79273c992 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssStatus;
import android.location.IGnssStatusListener;
import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@ import android.os.IBinder;
import android.stats.location.LocationStatsEnums;
import android.util.Log;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationUsageLogger;
@@ -35,10 +36,12 @@ import com.android.server.location.util.LocationUsageLogger;
*/
public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatusListener, Void> {
+ private final AppOpsHelper mAppOpsHelper;
private final LocationUsageLogger mLogger;
public GnssStatusProvider(Injector injector) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mLogger = injector.getLocationUsageLogger();
}
@@ -113,7 +116,8 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu
*/
public void onSvStatusChanged(GnssStatus gnssStatus) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onSvStatusChanged(gnssStatus);
} else {
return null;
@@ -126,7 +130,8 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu
*/
public void onNmeaReceived(long timestamp, String nmea) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onNmeaReceived(timestamp, nmea);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index f5889ceeed6a..528cf8acd5b3 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.os.Binder;
import android.os.Build;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -40,8 +41,8 @@ import java.util.function.Predicate;
* A base class to multiplex client listener registrations within system server. Registrations are
* divided into two categories, active registrations and inactive registrations, as defined by
* {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
- * {@link #updateRegistrations(Predicate)} or {@link #updateRegistration(Object, Predicate)} must be
- * invoked and return true for any registration whose active state may have changed.
+ * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
+ * whose active state may have changed.
*
* Callbacks invoked for various changes will always be ordered according to this lifecycle list:
*
@@ -217,7 +218,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
mRegistrations.put(key, registration);
}
-
if (wasEmpty) {
onRegister();
}
@@ -268,7 +268,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TKey key = mRegistrations.keyAt(i);
if (predicate.test(key)) {
removeRegistration(key, mRegistrations.valueAt(i));
@@ -287,7 +288,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
* completely at some later time.
*/
protected final void removeRegistration(@NonNull Object key,
- @NonNull ListenerRegistration registration) {
+ @NonNull ListenerRegistration<?, ?> registration) {
synchronized (mRegistrations) {
int index = mRegistrations.indexOfKey(key);
if (index < 0) {
@@ -353,7 +354,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
ArrayList<TRegistration> actives = new ArrayList<>(mRegistrations.size());
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
actives.add(registration);
@@ -395,7 +397,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
protected final void updateService(Predicate<TRegistration> predicate) {
synchronized (mRegistrations) {
boolean updateService = false;
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (predicate.test(registration) && registration.isActive()) {
updateService = true;
@@ -434,7 +437,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (predicate.test(registration)) {
onRegistrationActiveChanged(registration);
@@ -446,33 +450,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
}
- /**
- * Evaluates the predicate on the registration with the given key. The predicate should return
- * true if the active state of the registration may have changed as a result. Any
- * {@link #updateService()} invocations made while this method is executing will be deferred
- * until after the method is complete so as to avoid redundant work.
- */
- protected final void updateRegistration(TKey key, @NonNull Predicate<TRegistration> predicate) {
- synchronized (mRegistrations) {
- // since updating a registration can invoke a variety of callbacks, we need to ensure
- // those callbacks themselves do not re-enter, as this could lead to out-of-order
- // callbacks. note that try-with-resources ordering is meaningful here as well. we want
- // to close the reentrancy guard first, as this may generate additional service updates,
- // then close the update service buffer.
- long identity = Binder.clearCallingIdentity();
- try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
- ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
-
- TRegistration registration = mRegistrations.get(key);
- if (registration != null && predicate.test(registration)) {
- onRegistrationActiveChanged(registration);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
@GuardedBy("mRegistrations")
private void onRegistrationActiveChanged(TRegistration registration) {
if (Build.IS_DEBUGGABLE) {
@@ -511,7 +488,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
synchronized (mRegistrations) {
long identity = Binder.clearCallingIdentity();
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
ListenerOperation<TListener> operation = function.apply(registration);
@@ -537,7 +515,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
synchronized (mRegistrations) {
long identity = Binder.clearCallingIdentity();
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
execute(registration, operation);
@@ -571,7 +550,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
ipw.println("listeners:");
ipw.increaseIndent();
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
ipw.print(registration);
if (!registration.isActive()) {
@@ -612,23 +592,33 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
*/
private final class ReentrancyGuard implements AutoCloseable {
+ @GuardedBy("mRegistrations")
private int mGuardCount;
- private @Nullable ArrayList<Pair<Object, ListenerRegistration>> mScheduledRemovals;
+ @GuardedBy("mRegistrations")
+ private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
ReentrancyGuard() {
mGuardCount = 0;
mScheduledRemovals = null;
}
+ @GuardedBy("mRegistrations")
boolean isReentrant() {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mRegistrations));
+ }
return mGuardCount != 0;
}
- void markForRemoval(Object key, ListenerRegistration registration) {
+ @GuardedBy("mRegistrations")
+ void markForRemoval(Object key, ListenerRegistration<?, ?> registration) {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mRegistrations));
+ }
Preconditions.checkState(isReentrant());
if (mScheduledRemovals == null) {
- mScheduledRemovals = new ArrayList<>(mRegistrations.size());
+ mScheduledRemovals = new ArraySet<>(mRegistrations.size());
}
mScheduledRemovals.add(new Pair<>(key, registration));
}
@@ -640,7 +630,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
@Override
public void close() {
- ArrayList<Pair<Object, ListenerRegistration>> scheduledRemovals = null;
+ ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
Preconditions.checkState(mGuardCount > 0);
if (--mGuardCount == 0) {
@@ -650,8 +640,10 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
if (scheduledRemovals != null) {
try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
- for (int i = 0; i < scheduledRemovals.size(); i++) {
- Pair<Object, ListenerRegistration> pair = scheduledRemovals.get(i);
+ final int size = scheduledRemovals.size();
+ for (int i = 0; i < size; i++) {
+ Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt(
+ i);
removeRegistration(pair.first, pair.second);
}
}
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index e529a7d81b70..6a815ead9f9f 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -61,7 +61,7 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends
* Removes this registration. Does nothing if invoked before {@link #onRegister(Object)} or
* after {@link #onUnregister()}. It is safe to invoke this from within either function.
*/
- public void remove() {
+ public final void remove() {
Object key = mKey;
if (key != null) {
getOwner().removeRegistration(key, this);
diff --git a/services/core/java/com/android/server/location/util/AppOpsHelper.java b/services/core/java/com/android/server/location/util/AppOpsHelper.java
index 3e42f27da78c..1578289d53b4 100644
--- a/services/core/java/com/android/server/location/util/AppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/AppOpsHelper.java
@@ -16,15 +16,8 @@
package com.android.server.location.util;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-
-import android.app.AppOpsManager;
import android.location.util.identity.CallerIdentity;
-import com.android.server.location.LocationPermissions;
-import com.android.server.location.LocationPermissions.PermissionLevel;
-
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -70,84 +63,27 @@ public abstract class AppOpsHelper {
}
/**
- * Checks if the given identity may have locations delivered without noting that a location is
- * being delivered. This is a looser guarantee than
- * {@link #noteLocationAccess(CallerIdentity, int)}, and this function does not validate package
- * arguments and so should not be used with unvalidated arguments or before actually delivering
- * locations.
- *
- * @see AppOpsManager#checkOpNoThrow(int, int, String)
- */
- public final boolean checkLocationAccess(CallerIdentity callerIdentity,
- @PermissionLevel int permissionLevel) {
- if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
- return false;
- }
-
- return checkOpNoThrow(LocationPermissions.asAppOp(permissionLevel), callerIdentity);
- }
-
- /**
- * Notes location access to the given identity, ie, location delivery. This method should be
- * called right before a location is delivered, and if it returns false, the location should not
- * be delivered.
- */
- public final boolean noteLocationAccess(CallerIdentity identity,
- @PermissionLevel int permissionLevel) {
- if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
- return false;
- }
-
- return noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity);
- }
-
- /**
- * Notifies app ops that the given identity is using location at normal/low power levels. If
- * this function returns false, do not later call
- * {@link #stopLocationMonitoring(CallerIdentity)}.
+ * Starts the given appop.
*/
- public final boolean startLocationMonitoring(CallerIdentity identity) {
- return startOpNoThrow(OP_MONITOR_LOCATION, identity);
- }
+ public abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is no longer using location at normal/low power
- * levels.
+ * Finishes the given appop.
*/
- public final void stopLocationMonitoring(CallerIdentity identity) {
- finishOp(OP_MONITOR_LOCATION, identity);
- }
+ public abstract void finishOp(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is using location at high levels. If this function
- * returns false, do not later call {@link #stopLocationMonitoring(CallerIdentity)}.
+ * Checks the given appop.
*/
- public final boolean startHighPowerLocationMonitoring(CallerIdentity identity) {
- return startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity);
- }
+ public abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is no longer using location at high power levels.
+ * Notes the given appop (and may throw a security exception).
*/
- public final void stopHighPowerLocationMonitoring(CallerIdentity identity) {
- finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
- }
+ public abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
/**
- * Notes access to any mock location APIs. If this call returns false, access to the APIs should
- * silently fail.
+ * Notes the given appop.
*/
- public final boolean noteMockLocationAccess(CallerIdentity callerIdentity) {
- return noteOp(AppOpsManager.OP_MOCK_LOCATION, callerIdentity);
- }
-
- protected abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
- protected abstract void finishOp(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
+ public abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
}
diff --git a/services/core/java/com/android/server/location/util/Injector.java b/services/core/java/com/android/server/location/util/Injector.java
index e16df5dc26cd..379b303bbfc3 100644
--- a/services/core/java/com/android/server/location/util/Injector.java
+++ b/services/core/java/com/android/server/location/util/Injector.java
@@ -17,6 +17,7 @@
package com.android.server.location.util;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.LocationRequestStatistics;
/**
* Injects various location dependencies so that they may be controlled by tests.
@@ -30,15 +31,27 @@ public interface Injector {
/** Returns an AppOpsHelper. */
AppOpsHelper getAppOpsHelper();
+ /** Returns a LocationPermissionsHelper. */
+ LocationPermissionsHelper getLocationPermissionsHelper();
+
/** Returns a SettingsHelper. */
SettingsHelper getSettingsHelper();
/** Returns an AppForegroundHelper. */
AppForegroundHelper getAppForegroundHelper();
- /** Returns a LocationUsageLogger. */
- LocationUsageLogger getLocationUsageLogger();
+ /** Returns a LocationPowerSaveModeHelper. */
+ LocationPowerSaveModeHelper getLocationPowerSaveModeHelper();
+
+ /** Returns a ScreenInteractiveHelper. */
+ ScreenInteractiveHelper getScreenInteractiveHelper();
/** Returns a LocationAttributionHelper. */
LocationAttributionHelper getLocationAttributionHelper();
+
+ /** Returns a LocationUsageLogger. */
+ LocationUsageLogger getLocationUsageLogger();
+
+ /** Returns a LocationRequestStatistics. */
+ LocationRequestStatistics getLocationRequestStatistics();
}
diff --git a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
index 8fe09412c166..bc3ac0ff2e48 100644
--- a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
@@ -16,9 +16,16 @@
package com.android.server.location.util;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
import android.location.util.identity.CallerIdentity;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -83,7 +90,7 @@ public class LocationAttributionHelper {
i -> new ArraySet<>());
boolean empty = keySet.isEmpty();
if (keySet.add(new ProviderListener(provider, key)) && empty) {
- if (!mAppOpsHelper.startLocationMonitoring(identity)) {
+ if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
mAttributions.remove(identity);
}
}
@@ -99,7 +106,7 @@ public class LocationAttributionHelper {
if (keySet != null && keySet.remove(new ProviderListener(provider, key))
&& keySet.isEmpty()) {
mAttributions.remove(identity);
- mAppOpsHelper.stopLocationMonitoring(identity);
+ mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
}
}
@@ -113,14 +120,18 @@ public class LocationAttributionHelper {
i -> new ArraySet<>());
boolean empty = keySet.isEmpty();
if (keySet.add(new ProviderListener(provider, key)) && empty) {
- if (!mAppOpsHelper.startHighPowerLocationMonitoring(identity)) {
+ if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
+ if (D) {
+ Log.v(TAG, "starting high power location attribution for " + identity);
+ }
+ } else {
mHighPowerAttributions.remove(identity);
}
}
}
/**
- * Report high power location usage has stopped for the given caller on the given provider,
+ * Report high power location usage has stopped for the given caller on the given provider,
* with a unique key.
*/
public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String provider,
@@ -128,8 +139,11 @@ public class LocationAttributionHelper {
Set<ProviderListener> keySet = mHighPowerAttributions.get(identity);
if (keySet != null && keySet.remove(new ProviderListener(provider, key))
&& keySet.isEmpty()) {
+ if (D) {
+ Log.v(TAG, "stopping high power location attribution for " + identity);
+ }
mHighPowerAttributions.remove(identity);
- mAppOpsHelper.stopHighPowerLocationMonitoring(identity);
+ mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
}
}
}
diff --git a/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
new file mode 100644
index 000000000000..daf56797c0c9
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
@@ -0,0 +1,107 @@
+/*
+ * 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.location.util;
+
+import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+
+import android.location.util.identity.CallerIdentity;
+
+import com.android.server.location.LocationPermissions;
+import com.android.server.location.LocationPermissions.PermissionLevel;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides helpers and listeners for appops.
+ */
+public abstract class LocationPermissionsHelper {
+
+ /**
+ * Listener for current user changes.
+ */
+ public interface LocationPermissionsListener {
+
+ /**
+ * Called when something has changed about location permissions for the given package.
+ */
+ void onLocationPermissionsChanged(String packageName);
+
+ /**
+ * Called when something has changed about location permissions for the given uid.
+ */
+ void onLocationPermissionsChanged(int uid);
+ }
+
+ private final CopyOnWriteArrayList<LocationPermissionsListener> mListeners;
+ private final AppOpsHelper mAppOps;
+
+ public LocationPermissionsHelper(AppOpsHelper appOps) {
+ mListeners = new CopyOnWriteArrayList<>();
+ mAppOps = appOps;
+
+ mAppOps.addListener(this::onAppOpsChanged);
+ }
+
+ protected final void notifyLocationPermissionsChanged(String packageName) {
+ for (LocationPermissionsListener listener : mListeners) {
+ listener.onLocationPermissionsChanged(packageName);
+ }
+ }
+
+ protected final void notifyLocationPermissionsChanged(int uid) {
+ for (LocationPermissionsListener listener : mListeners) {
+ listener.onLocationPermissionsChanged(uid);
+ }
+ }
+
+ private void onAppOpsChanged(String packageName) {
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ /**
+ * Adds a listener for location permissions events. Callbacks occur on an unspecified thread.
+ */
+ public final void addListener(LocationPermissionsListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for location permissions events.
+ */
+ public final void removeListener(LocationPermissionsListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Returns true if the given identity may access location at the given permissions level, taking
+ * into account both permissions and appops.
+ */
+ public final boolean hasLocationPermissions(@PermissionLevel int permissionLevel,
+ CallerIdentity identity) {
+ if (permissionLevel == PERMISSION_NONE) {
+ return false;
+ }
+
+ if (!hasPermission(LocationPermissions.asPermission(permissionLevel), identity)) {
+ return false;
+ }
+
+ return mAppOps.checkOpNoThrow(permissionLevel, identity);
+ }
+
+ protected abstract boolean hasPermission(String permission, CallerIdentity callerIdentity);
+}
diff --git a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
new file mode 100644
index 000000000000..a9a8c50f11dc
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
@@ -0,0 +1,71 @@
+/*
+ * 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.location.util;
+
+import android.os.PowerManager.LocationPowerSaveMode;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public abstract class LocationPowerSaveModeHelper {
+
+ /**
+ * Listener for location power save mode changes.
+ */
+ public interface LocationPowerSaveModeChangedListener {
+ /**
+ * Called when the location power save mode changes.
+ */
+ void onLocationPowerSaveModeChanged(@LocationPowerSaveMode int locationPowerSaveMode);
+ }
+
+ private final CopyOnWriteArrayList<LocationPowerSaveModeChangedListener> mListeners;
+
+ public LocationPowerSaveModeHelper() {
+ mListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /**
+ * Add a listener for changes to location power save mode. Callbacks occur on an unspecified
+ * thread.
+ */
+ public final void addListener(LocationPowerSaveModeChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for changes to location power save mode.
+ */
+ public final void removeListener(LocationPowerSaveModeChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ protected final void notifyLocationPowerSaveModeChanged(
+ @LocationPowerSaveMode int locationPowerSaveMode) {
+ for (LocationPowerSaveModeChangedListener listener : mListeners) {
+ listener.onLocationPowerSaveModeChanged(locationPowerSaveMode);
+ }
+ }
+
+ /**
+ * Returns the current location power save mode.
+ */
+ @LocationPowerSaveMode
+ public abstract int getLocationPowerSaveMode();
+}
diff --git a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
new file mode 100644
index 000000000000..d47bce31ed23
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
@@ -0,0 +1,67 @@
+/*
+ * 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.location.util;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public abstract class ScreenInteractiveHelper {
+
+ /**
+ * Listener for screen interactive changes.
+ */
+ public interface ScreenInteractiveChangedListener {
+ /**
+ * Called when the screen interative state changes.
+ */
+ void onScreenInteractiveChanged(boolean isInteractive);
+ }
+
+ private final CopyOnWriteArrayList<ScreenInteractiveChangedListener> mListeners;
+
+ public ScreenInteractiveHelper() {
+ mListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /**
+ * Add a listener for changes to screen interactive state. Callbacks occur on an unspecified
+ * thread.
+ */
+ public final void addListener(ScreenInteractiveChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for changes to screen interactive state.
+ */
+ public final void removeListener(ScreenInteractiveChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ protected final void notifyScreenInteractiveChanged(boolean interactive) {
+ for (ScreenInteractiveChangedListener listener : mListeners) {
+ listener.onScreenInteractiveChanged(interactive);
+ }
+ }
+
+ /**
+ * Returns true if the screen is currently interactive, and false otherwise.
+ */
+ public abstract boolean isInteractive();
+}
diff --git a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
index a95383695ae8..cfb7697a8dfc 100644
--- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
@@ -57,7 +57,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -75,7 +75,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+ public void finishOp(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -91,7 +91,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -106,7 +106,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -123,7 +123,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
new file mode 100644
index 000000000000..b9c0ddef04ab
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
@@ -0,0 +1,65 @@
+/*
+ * 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.location.util;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.content.Context;
+import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
+
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for location permissions, including appops.
+ */
+public class SystemLocationPermissionsHelper extends LocationPermissionsHelper {
+
+ private final Context mContext;
+
+ private boolean mInited;
+
+ public SystemLocationPermissionsHelper(Context context, AppOpsHelper appOps) {
+ super(appOps);
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mInited) {
+ return;
+ }
+
+ mContext.getPackageManager().addOnPermissionsChangeListener(
+ uid -> {
+ // invoked on ui thread, move to fg thread so ui thread isn't blocked
+ FgThread.getHandler().post(() -> notifyLocationPermissionsChanged(uid));
+ });
+ mInited = true;
+ }
+
+ @Override
+ protected boolean hasPermission(String permission, CallerIdentity callerIdentity) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mContext.checkPermission(permission, callerIdentity.getPid(),
+ callerIdentity.getUid()) == PERMISSION_GRANTED;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
new file mode 100644
index 000000000000..c8d8202157f0
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
@@ -0,0 +1,87 @@
+/*
+ * 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.location.util;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public class SystemLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper implements
+ Consumer<PowerSaveState> {
+
+ private final Context mContext;
+ private boolean mReady;
+
+ @LocationPowerSaveMode
+ private volatile int mLocationPowerSaveMode;
+
+ public SystemLocationPowerSaveModeHelper(Context context) {
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mReady) {
+ return;
+ }
+
+ LocalServices.getService(PowerManagerInternal.class).registerLowPowerModeObserver(
+ PowerManager.ServiceType.LOCATION, this);
+ mLocationPowerSaveMode = Objects.requireNonNull(
+ mContext.getSystemService(PowerManager.class)).getLocationPowerSaveMode();
+
+ mReady = true;
+ }
+
+ @Override
+ public void accept(PowerSaveState powerSaveState) {
+ int locationPowerSaveMode;
+ if (!powerSaveState.batterySaverEnabled) {
+ locationPowerSaveMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+ } else {
+ locationPowerSaveMode = powerSaveState.locationMode;
+ }
+
+ if (locationPowerSaveMode == mLocationPowerSaveMode) {
+ return;
+ }
+
+ mLocationPowerSaveMode = locationPowerSaveMode;
+
+ // invoked on ui thread, move to fg thread so we don't block the ui thread
+ FgThread.getHandler().post(() -> notifyLocationPowerSaveModeChanged(locationPowerSaveMode));
+ }
+
+ @LocationPowerSaveMode
+ @Override
+ public int getLocationPowerSaveMode() {
+ Preconditions.checkState(mReady);
+ return mLocationPowerSaveMode;
+ }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
new file mode 100644
index 000000000000..70cedac20868
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
@@ -0,0 +1,85 @@
+/*
+ * 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.location.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public class SystemScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+ private final Context mContext;
+
+ private boolean mReady;
+
+ private volatile boolean mIsInteractive;
+
+ public SystemScreenInteractiveHelper(Context context) {
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mReady) {
+ return;
+ }
+
+ IntentFilter screenIntentFilter = new IntentFilter();
+ screenIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ screenIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean interactive;
+ if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+ interactive = true;
+ } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ interactive = false;
+ } else {
+ return;
+ }
+
+ onScreenInteractiveChanged(interactive);
+ }
+ }, UserHandle.ALL, screenIntentFilter, null, FgThread.getHandler());
+
+ mReady = true;
+ }
+
+ private void onScreenInteractiveChanged(boolean interactive) {
+ if (interactive == mIsInteractive) {
+ return;
+ }
+
+ mIsInteractive = interactive;
+ notifyScreenInteractiveChanged(interactive);
+ }
+
+ @Override
+ public boolean isInteractive() {
+ Preconditions.checkState(mReady);
+ return mIsInteractive;
+ }
+}
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index b460cb5b23ea..2e2d812c058e 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -2,6 +2,7 @@ elaurent@google.com
hdmoon@google.com
insun@google.com
jaewan@google.com
+jinpark@google.com
klhyun@google.com
lajos@google.com
sungsoo@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 00cb22e9d4b5..391a08db6716 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -459,12 +459,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final int returnCode = args.argi1;
args.recycle();
- final boolean showNotification;
- synchronized (mLock) {
- showNotification = isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked();
- }
sendOnPackageInstalled(mContext, statusReceiver, sessionId,
- showNotification, userId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
packageName, returnCode, message, extras);
break;
@@ -494,8 +490,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
- @GuardedBy("mLock")
- private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
+ private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwner() {
+ assertNotLocked("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
+ // It is safe to access mInstallerUid and mInstallSource without lock
+ // because they are immutable after sealing.
+ assertSealed("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
if (userId != UserHandle.getUserId(mInstallerUid)) {
return false;
}
@@ -513,12 +512,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
*
* @return {@code true} iff we need to ask to confirm the permissions?
*/
- @GuardedBy("mLock")
- private boolean needToAskForPermissionsLocked() {
- if (mPermissionsManuallyAccepted) {
- return false;
+ private boolean needToAskForPermissions() {
+ final String packageName;
+ synchronized (mLock) {
+ if (mPermissionsManuallyAccepted) {
+ return false;
+ }
+ packageName = mPackageName;
}
+ // It is safe to access mInstallerUid and mInstallSource without lock
+ // because they are immutable after sealing.
final boolean isInstallPermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
@@ -528,7 +532,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean isUpdatePermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
- final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
+ final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
final boolean isPermissionGranted = isInstallPermissionGranted
|| (isUpdatePermissionGranted && targetPackageUid != -1)
|| (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
@@ -540,7 +544,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Device owners and affiliated profile owners are allowed to silently install packages, so
// the permission check is waived if the installer is the device owner.
return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
- || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
+ || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner());
}
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
@@ -740,6 +744,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private void assertNotLocked(String cookie) {
+ if (Thread.holdsLock(mLock)) {
+ throw new IllegalStateException(cookie + " is holding mLock");
+ }
+ }
+
+ private void assertSealed(String cookie) {
+ if (!isSealed()) {
+ throw new IllegalStateException(cookie + " before sealing");
+ }
+ }
+
@GuardedBy("mLock")
private void assertPreparedAndNotSealedLocked(String cookie) {
assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
@@ -1693,11 +1709,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void handleInstall() {
- final boolean needsLogging;
- synchronized (mLock) {
- needsLogging = isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked();
- }
- if (needsLogging) {
+ if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
.setAdmin(mInstallSource.installerPackageName)
@@ -1724,9 +1736,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
try {
- synchronized (mLock) {
- installNonStagedLocked(childSessions);
- }
+ installNonStaged(childSessions);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
@@ -1735,11 +1745,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- @GuardedBy("mLock")
- private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
+ private void installNonStaged(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
final PackageManagerService.ActiveInstallSession installingSession =
- makeSessionActiveLocked();
+ makeSessionActive();
if (installingSession == null) {
return;
}
@@ -1752,7 +1761,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final PackageInstallerSession session = childSessions.get(i);
try {
final PackageManagerService.ActiveInstallSession installingChildSession =
- session.makeSessionActiveLocked();
+ session.makeSessionActive();
if (installingChildSession != null) {
installingChildSessions.add(installingChildSession);
}
@@ -1762,8 +1771,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
if (!success) {
- sendOnPackageInstalled(mContext, mRemoteStatusReceiver, sessionId,
- isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null,
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
}
@@ -1778,41 +1791,58 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null
* in case permissions need to be requested before install can proceed.
*/
- @GuardedBy("mLock")
- private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
+ private PackageManagerService.ActiveInstallSession makeSessionActive()
throws PackageManagerException {
- if (mRelinquished) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session relinquished");
+ assertNotLocked("makeSessionActive");
+
+ synchronized (mLock) {
+ if (mRelinquished) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session relinquished");
+ }
+ if (mDestroyed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session destroyed");
+ }
+ if (!mSealed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session not sealed");
+ }
}
- if (mDestroyed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
+
+ if (!params.isMultiPackage && needToAskForPermissions()) {
+ // User needs to confirm installation;
+ // give installer an intent they can use to involve
+ // user.
+ final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
+ intent.setPackage(mPm.getPackageInstallerPackageName());
+ intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
+
+ // Commit was keeping session marked as active until now; release
+ // that extra refcount so session appears idle.
+ closeInternal(false);
+ return null;
}
- if (!mSealed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
+
+ synchronized (mLock) {
+ return makeSessionActiveLocked();
}
+ }
+ @GuardedBy("mLock")
+ private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
+ throws PackageManagerException {
if (!params.isMultiPackage) {
Objects.requireNonNull(mPackageName);
Objects.requireNonNull(mSigningDetails);
Objects.requireNonNull(mResolvedBaseFile);
- if (needToAskForPermissionsLocked()) {
- // User needs to confirm installation;
- // give installer an intent they can use to involve
- // user.
- final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
- intent.setPackage(mPm.getPackageInstallerPackageName());
- intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-
- sendOnUserActionRequired(mContext, mRemoteStatusReceiver, sessionId, intent);
-
- // Commit was keeping session marked as active until now; release
- // that extra refcount so session appears idle.
- closeInternal(false);
- return null;
- }
-
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
@@ -2438,7 +2468,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* Determine if creating hard links between source and destination is
* possible. That is, do they all live on the same underlying device.
*/
- private boolean isLinkPossible(List<File> fromFiles, File toDir) {
+ private static boolean isLinkPossible(List<File> fromFiles, File toDir) {
try {
final StructStat toStat = Os.stat(toDir.getAbsolutePath());
for (File fromFile : fromFiles) {
@@ -2915,7 +2945,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
// Don't fail or commit the session. Allow caller to commit again.
- sendPendingStreaming("DataLoader unavailable");
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendPendingStreaming(mContext, statusReceiver, sessionId,
+ "DataLoader unavailable");
break;
}
case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
@@ -2929,7 +2964,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
} catch (RemoteException e) {
// In case of streaming failure we don't want to fail or commit the session.
// Just return from this method and allow caller to commit again.
- sendPendingStreaming(e.getMessage());
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendPendingStreaming(mContext, statusReceiver, sessionId, e.getMessage());
}
}
};
@@ -3004,16 +3043,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
detailMessage).sendToTarget();
}
+ @GuardedBy("mLock")
+ private int[] getChildSessionIdsLocked() {
+ final int[] childSessionIds = mChildSessionIds.copyKeys();
+ return childSessionIds != null ? childSessionIds : EMPTY_CHILD_SESSION_ARRAY;
+ }
+
@Override
public int[] getChildSessionIds() {
- final int[] childSessionIds;
synchronized (mLock) {
- childSessionIds = mChildSessionIds.copyKeys();
+ return getChildSessionIdsLocked();
}
- if (childSessionIds != null) {
- return childSessionIds;
- }
- return EMPTY_CHILD_SESSION_ARRAY;
}
private boolean canBeAddedAsChild(int parentCandidate) {
@@ -3323,6 +3363,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.decreaseIndent();
}
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
private static void sendOnUserActionRequired(Context context, IntentSender target,
int sessionId, Intent intent) {
final Intent fillIn = new Intent();
@@ -3335,6 +3378,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
boolean showNotification, int userId, String basePackageName, int returnCode,
String msg, Bundle extras) {
@@ -3375,13 +3421,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- private void sendPendingStreaming(@Nullable String cause) {
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
-
- if (statusReceiver == null) {
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
+ private static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
+ @Nullable String cause) {
+ if (target == null) {
Slog.e(TAG, "Missing receiver for pending streaming status.");
return;
}
@@ -3396,7 +3441,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
}
try {
- statusReceiver.sendIntent(mContext, 0, intent, null, null);
+ target.sendIntent(context, 0, intent, null, null);
} catch (IntentSender.SendIntentException ignored) {
}
}
@@ -3470,10 +3515,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (stageCid != null) {
writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
}
- writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
- writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
- writeBooleanAttribute(out, ATTR_DESTROYED, isDestroyed());
- writeBooleanAttribute(out, ATTR_SEALED, isSealed());
+ writeBooleanAttribute(out, ATTR_PREPARED, mPrepared);
+ writeBooleanAttribute(out, ATTR_COMMITTED, mCommitted);
+ writeBooleanAttribute(out, ATTR_DESTROYED, mDestroyed);
+ writeBooleanAttribute(out, ATTR_SEALED, mSealed);
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
@@ -3535,7 +3580,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
params.appIconLastModified = appIconFile.lastModified();
}
- final int[] childSessionIds = getChildSessionIds();
+ final int[] childSessionIds = getChildSessionIdsLocked();
for (int childSessionId : childSessionIds) {
out.startTag(null, TAG_CHILD_SESSION);
writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
@@ -3543,7 +3588,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
final InstallationFile[] files = getInstallationFilesLocked();
- for (InstallationFile file : getInstallationFilesLocked()) {
+ for (InstallationFile file : files) {
out.startTag(null, TAG_SESSION_FILE);
writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
writeStringAttribute(out, ATTR_NAME, file.getName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bd12fd5f5d9a..2854b337fd29 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -651,6 +651,23 @@ public class PackageManagerService extends IPackageManager.Stub
private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE =
150857253;
+ /**
+ * Apps targeting Android S and above need to declare dependencies to the public native
+ * shared libraries that are defined by the device maker using {@code uses-native-library} tag
+ * in its {@code AndroidManifest.xml}.
+ *
+ * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
+ * the package manager rejects to install the app. The dependency can be specified as optional
+ * using {@code android:required} attribute in the tag, in which case failing to satisfy the
+ * dependency doesn't stop the installation.
+ * <p>Once installed, an app is provided with only the native shared libraries that are
+ * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
+ * in the app manifest will fail even if it actually exists on the device.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
+
public static final String PLATFORM_PACKAGE_NAME = "android";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
@@ -2952,9 +2969,8 @@ public class PackageManagerService extends IPackageManager.Stub
= systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
- String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
- addBuiltInSharedLibraryLocked(entry.filename, name);
+ addBuiltInSharedLibraryLocked(libConfig.valueAt(i));
}
// Now that we have added all the libraries, iterate again to add dependency
@@ -10486,6 +10502,19 @@ public class PackageManagerService extends IPackageManager.Stub
null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
+ // TODO(b/160928779) gate this behavior using ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES
+ if (pkg.getTargetSdkVersion() > 30) {
+ if (!pkg.getUsesNativeLibraries().isEmpty() && pkg.getTargetSdkVersion() > 30) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
+ null, pkg.getPackageName(), true, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ }
+ if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
+ null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ }
+ }
return usesLibraryInfos;
}
@@ -12177,15 +12206,16 @@ public class PackageManagerService extends IPackageManager.Stub
}
@GuardedBy("mLock")
- private boolean addBuiltInSharedLibraryLocked(String path, String name) {
- if (nonStaticSharedLibExistsLocked(name)) {
+ private boolean addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
+ if (nonStaticSharedLibExistsLocked(entry.name)) {
return false;
}
- SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, null, null, name,
- (long) SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
- new VersionedPackage(PLATFORM_PACKAGE_NAME, (long) 0),
- null, null);
+ SharedLibraryInfo libraryInfo = new SharedLibraryInfo(entry.filename, null, null,
+ entry.name, (long) SharedLibraryInfo.VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_BUILTIN,
+ new VersionedPackage(PLATFORM_PACKAGE_NAME, (long)0), null, null,
+ entry.isNative);
commitSharedLibraryInfoLocked(libraryInfo);
return true;
@@ -21900,7 +21930,11 @@ public class PackageManagerService extends IPackageManager.Stub
pw.print(" -> ");
}
if (libraryInfo.getPath() != null) {
- pw.print(" (jar) ");
+ if (libraryInfo.isNative()) {
+ pw.print(" (so) ");
+ } else {
+ pw.print(" (jar) ");
+ }
pw.print(libraryInfo.getPath());
} else {
pw.print(" (apk) ");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 13b927e7d9f4..7106499f9b56 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4784,6 +4784,23 @@ public final class Settings {
}
}
+ List<String> usesNativeLibraries = pkg.getUsesNativeLibraries();
+ if (usesNativeLibraries.size() > 0) {
+ pw.print(prefix); pw.println(" usesNativeLibraries:");
+ for (int i=0; i< usesNativeLibraries.size(); i++) {
+ pw.print(prefix); pw.print(" "); pw.println(usesNativeLibraries.get(i));
+ }
+ }
+
+ List<String> usesOptionalNativeLibraries = pkg.getUsesOptionalNativeLibraries();
+ if (usesOptionalNativeLibraries.size() > 0) {
+ pw.print(prefix); pw.println(" usesOptionalNativeLibraries:");
+ for (int i=0; i< usesOptionalNativeLibraries.size(); i++) {
+ pw.print(prefix); pw.print(" ");
+ pw.println(usesOptionalNativeLibraries.get(i));
+ }
+ }
+
List<String> usesLibraryFiles = ps.getPkgState().getUsesLibraryFiles();
if (usesLibraryFiles.size() > 0) {
pw.print(prefix); pw.println(" usesLibraryFiles:");
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index c9e0bb467ce4..39784cf32cea 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -252,6 +252,19 @@ public interface AndroidPackage extends PkgAppInfo, PkgPackageInfo, ParsingPacka
@NonNull
List<String> getUsesOptionalLibraries();
+ /** @see R.styleabele#AndroidManifestUsesNativeLibrary */
+ @NonNull
+ List<String> getUsesNativeLibraries();
+
+ /**
+ * Like {@link #getUsesNativeLibraries()}, but marked optional by setting
+ * {@link R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is
+ * expected to handle absence manually.
+ * @see R.styleable#AndroidManifestUsesNativeLibrary
+ */
+ @NonNull
+ List<String> getUsesOptionalNativeLibraries();
+
/**
* TODO(b/135203078): Move static library stuff to an inner data class
* @see R.styleable#AndroidManifestUsesStaticLibrary
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index f5eed30a19bf..f5e1602ee6be 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -51,6 +51,7 @@ import android.app.AppGlobals;
import android.app.GrantedUriPermission;
import android.app.IUriGrantsManager;
import android.content.ClipData;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -698,6 +699,11 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
final UriPermission perm = findOrCreateUriPermissionLocked(
sourcePkg, targetPkg, targetUid, grantUri);
perm.initPersistedModes(modeFlags, createdTime);
+ mPmInternal.grantImplicitAccess(
+ targetUserId, null,
+ UserHandle.getAppId(targetUid),
+ pi.applicationInfo.uid,
+ false /* direct */);
}
} else {
Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
@@ -1171,6 +1177,9 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
// grant, we can skip generating any bookkeeping; when any advanced
// features have been requested, we proceed below to make sure the
// provider supports granting permissions
+ mPmInternal.grantImplicitAccess(
+ UserHandle.getUserId(targetUid), null,
+ UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false);
return -1;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 55962fc883d9..90f87b16e70d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1160,9 +1160,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
};
- private Runnable mTryToRebindRunnable = () -> {
- tryToRebind();
- };
+ private Runnable mTryToRebindRunnable = this::tryToRebind;
WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
mInfo = info;
@@ -1295,14 +1293,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// a short time in the future, specifically to allow any pending package
// update message on this same looper thread to be processed.
if (!mWallpaper.wallpaperUpdating) {
- mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
+ mContext.getMainThreadHandler().postDelayed(mDisconnectRunnable,
1000);
}
}
}
}
- public void scheduleTimeoutLocked() {
+ private void scheduleTimeoutLocked() {
// If we didn't reset it right away, do so after we couldn't connect to
// it for an extended amount of time to avoid having a black wallpaper.
final Handler fgHandler = FgThread.getHandler();
@@ -1342,11 +1340,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- private void processDisconnect(final ServiceConnection connection) {
+ private Runnable mDisconnectRunnable = () -> {
synchronized (mLock) {
// The wallpaper disappeared. If this isn't a system-default one, track
// crashes and fall back to default if it continues to misbehave.
- if (connection == mWallpaper.connection) {
+ if (this == mWallpaper.connection) {
final ComponentName wpService = mWallpaper.wallpaperComponent;
if (!mWallpaper.wallpaperUpdating
&& mWallpaper.userId == mCurrentUserId
@@ -1374,7 +1372,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
}
- }
+ };
/**
* Called by a live wallpaper if its colors have changed.
@@ -2786,6 +2784,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
WallpaperConnection.DisplayConnector::disconnectLocked);
wallpaper.connection.mService = null;
wallpaper.connection.mDisplayConnector.clear();
+
+ FgThread.getHandler().removeCallbacks(wallpaper.connection.mResetRunnable);
+ mContext.getMainThreadHandler().removeCallbacks(
+ wallpaper.connection.mDisconnectRunnable);
+ mContext.getMainThreadHandler().removeCallbacks(
+ wallpaper.connection.mTryToRebindRunnable);
+
wallpaper.connection = null;
if (wallpaper == mLastWallpaper) mLastWallpaper = null;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 0cd7ffce2ed4..04b1edc3eede 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1446,7 +1446,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mService.deferWindowLayout();
try {
stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- stack.setBounds(null);
+ if (stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ stack.setBounds(null);
+ }
toDisplay.getDefaultTaskDisplayArea().positionTaskBehindHome(stack);
// Follow on the workaround: activities are kept force hidden till the new windowing
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c6b93d6ca4f4..7ec819f13e96 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3570,6 +3570,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
+ /**
+ * Persist modified values to disk by calling {@link #saveSettingsLocked} for each
+ * affected user ID.
+ */
+ @GuardedBy("getLockObject()")
+ private void saveSettingsForUsersLocked(Set<Integer> affectedUserIds) {
+ for (int userId : affectedUserIds) {
+ saveSettingsLocked(userId);
+ }
+ }
+
private void saveSettingsLocked(int userHandle) {
DevicePolicyData policy = getUserData(userHandle);
JournaledFile journal = makeJournaledFile(userHandle);
@@ -4785,13 +4796,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Updates a flag that tells us whether the user's password currently satisfies the
- * requirements set by all of the user's active admins. The flag is updated both in memory
- * and persisted to disk by calling {@link #saveSettingsLocked}, for the value of the flag
- * be the correct one upon boot.
- * This should be called whenever the password or the admin policies have changed.
+ * requirements set by all of the user's active admins.
+ * This should be called whenever the password or the admin policies have changed. The caller
+ * is responsible for calling {@link #saveSettingsLocked} to persist the change.
+ *
+ * @return the set of user IDs that have been affected
*/
@GuardedBy("getLockObject()")
- private void updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+ private Set<Integer> updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
final int credentialOwner = getCredentialOwner(userHandle, parent);
DevicePolicyData policy = getUserData(credentialOwner);
PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
@@ -4801,9 +4814,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
metrics, userHandle, parent);
if (newCheckpoint != policy.mPasswordValidAtLastCheckpoint) {
policy.mPasswordValidAtLastCheckpoint = newCheckpoint;
- saveSettingsLocked(credentialOwner);
+ affectedUserIds.add(credentialOwner);
}
}
+ return affectedUserIds;
}
/**
@@ -6175,7 +6189,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void removeCaApprovalsIfNeeded(int userId) {
+ private Set<Integer> removeCaApprovalsIfNeeded(int userId) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
for (UserInfo userInfo : mUserManager.getProfiles(userId)) {
boolean isSecure = mLockPatternUtils.isSecure(userInfo.id);
if (userInfo.isManagedProfile()){
@@ -6184,11 +6199,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!isSecure) {
synchronized (getLockObject()) {
getUserData(userInfo.id).mAcceptedCaCertificates.clear();
- saveSettingsLocked(userInfo.id);
+ affectedUserIds.add(userInfo.id);
}
mCertificateMonitor.onCertificateApprovalsChanged(userId);
}
}
+ return affectedUserIds;
}
@Override
@@ -7458,42 +7474,45 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
DevicePolicyData policy = getUserData(userId);
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
synchronized (getLockObject()) {
policy.mFailedPasswordAttempts = 0;
- updatePasswordValidityCheckpointLocked(userId, /* parent */ false);
- saveSettingsLocked(userId);
- updatePasswordExpirationsLocked(userId);
+ affectedUserIds.add(userId);
+ affectedUserIds.addAll(updatePasswordValidityCheckpointLocked(
+ userId, /* parent */ false));
+ affectedUserIds.addAll(updatePasswordExpirationsLocked(userId));
setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
// Send a broadcast to each profile using this password as its primary unlock.
sendAdminCommandForLockscreenPoliciesLocked(
DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
+
+ affectedUserIds.addAll(removeCaApprovalsIfNeeded(userId));
+ saveSettingsForUsersLocked(affectedUserIds);
}
- removeCaApprovalsIfNeeded(userId);
}
/**
* Called any time the device password is updated. Resets all password expiration clocks.
+ *
+ * @return the set of user IDs that have been affected
*/
- private void updatePasswordExpirationsLocked(int userHandle) {
- ArraySet<Integer> affectedUserIds = new ArraySet<Integer>();
+ private Set<Integer> updatePasswordExpirationsLocked(int userHandle) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(
userHandle, /* parent */ false);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
+ for (int i = 0; i < admins.size(); i++) {
ActiveAdmin admin = admins.get(i);
if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
affectedUserIds.add(admin.getUserHandle().getIdentifier());
long timeout = admin.passwordExpirationTimeout;
- long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
- admin.passwordExpirationDate = expiration;
+ admin.passwordExpirationDate =
+ timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
}
}
- for (int affectedUserId : affectedUserIds) {
- saveSettingsLocked(affectedUserId);
- }
+ return affectedUserIds;
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
index da794da7f9c9..e947e89c4f94 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
@@ -57,7 +57,7 @@ public class FakeAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
if (!myAppOp.mAllowed) {
return false;
@@ -68,20 +68,20 @@ public class FakeAppOpsHelper extends AppOpsHelper {
}
@Override
- protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+ public void finishOp(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
Preconditions.checkState(myAppOp.mStarted);
myAppOp.mStarted = false;
}
@Override
- protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
return myAppOp.mAllowed;
}
@Override
- protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
if (!noteOpNoThrow(appOp, callerIdentity)) {
throw new SecurityException(
"noteOp not allowed for op " + appOp + " and caller " + callerIdentity);
@@ -91,7 +91,7 @@ public class FakeAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
if (!myAppOp.mAllowed) {
return false;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
new file mode 100644
index 000000000000..e7d7e310d1e4
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.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.location.util;
+
+import android.location.util.identity.CallerIdentity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Version of LocationPermissionsHelper for testing. All permissions are granted unless notified
+ * otherwise.
+ */
+public class FakeLocationPermissionsHelper extends LocationPermissionsHelper {
+
+ private final HashMap<String, Set<String>> mRevokedPermissions;
+
+ public FakeLocationPermissionsHelper(AppOpsHelper appOps) {
+ super(appOps);
+ mRevokedPermissions = new HashMap<>();
+ }
+
+ public void grantPermission(String packageName, String permission) {
+ getRevokedPermissionsList(packageName).remove(permission);
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ public void revokePermission(String packageName, String permission) {
+ getRevokedPermissionsList(packageName).add(permission);
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ protected boolean hasPermission(String permission, CallerIdentity identity) {
+ return !getRevokedPermissionsList(identity.getPackageName()).contains(permission);
+ }
+
+ private Set<String> getRevokedPermissionsList(String packageName) {
+ return mRevokedPermissions.computeIfAbsent(packageName, p -> new HashSet<>());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
new file mode 100644
index 000000000000..3ead5d4f214d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
@@ -0,0 +1,49 @@
+/*
+ * 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.location.util;
+
+import android.os.IPowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+
+/**
+ * Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
+ * change".
+ */
+public class FakeLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper {
+
+ @LocationPowerSaveMode
+ private int mLocationPowerSaveMode;
+
+ public FakeLocationPowerSaveModeHelper() {
+ mLocationPowerSaveMode = IPowerManager.LOCATION_MODE_NO_CHANGE;
+ }
+
+ public void setLocationPowerSaveMode(int locationPowerSaveMode) {
+ if (locationPowerSaveMode == mLocationPowerSaveMode) {
+ return;
+ }
+
+ mLocationPowerSaveMode = locationPowerSaveMode;
+ notifyLocationPowerSaveModeChanged(locationPowerSaveMode);
+ }
+
+ @LocationPowerSaveMode
+ @Override
+ public int getLocationPowerSaveMode() {
+ return mLocationPowerSaveMode;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
new file mode 100644
index 000000000000..df697fa1a03c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
@@ -0,0 +1,42 @@
+/*
+ * 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.location.util;
+
+/**
+ * Version of ScreenInteractiveHelper for testing. Screen is initialized as interactive (on).
+ */
+public class FakeScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+ private boolean mIsInteractive;
+
+ public FakeScreenInteractiveHelper() {
+ mIsInteractive = true;
+ }
+
+ public void setScreenInteractive(boolean interactive) {
+ if (interactive == mIsInteractive) {
+ return;
+ }
+
+ mIsInteractive = interactive;
+ notifyScreenInteractiveChanged(interactive);
+ }
+
+ public boolean isInteractive() {
+ return mIsInteractive;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
index 726b1b82b699..1d0523f18008 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
@@ -90,7 +90,7 @@ public class FakeSettingsHelper extends SettingsHelper {
@Override
public boolean isLocationEnabled(int userId) {
- return mLocationEnabledSetting.getValue(Boolean.class);
+ return mLocationEnabledSetting.getValue(userId, Boolean.class);
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
index 336e28c879ba..f5978da416be 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
@@ -16,7 +16,6 @@
package com.android.server.location.util;
-import android.os.Process;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.SparseArray;
@@ -27,19 +26,21 @@ import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
/**
- * Version of UserInfoHelper for testing. The user this code is running under is set as the current
- * user by default, with no profiles.
+ * Version of UserInfoHelper for testing. By default there is one user that starts in a running
+ * state with a userId of 0;
*/
public class FakeUserInfoHelper extends UserInfoHelper {
+ public static final int DEFAULT_USERID = 0;
+
private final IntArray mRunningUserIds;
private final SparseArray<IntArray> mProfiles;
private int mCurrentUserId;
public FakeUserInfoHelper() {
- mCurrentUserId = Process.myUserHandle().getIdentifier();
- mRunningUserIds = IntArray.wrap(new int[]{mCurrentUserId});
+ mCurrentUserId = DEFAULT_USERID;
+ mRunningUserIds = IntArray.wrap(new int[]{DEFAULT_USERID});
mProfiles = new SparseArray<>();
}
@@ -67,6 +68,10 @@ public class FakeUserInfoHelper extends UserInfoHelper {
dispatchOnUserStopped(userId);
}
+ public void setCurrentUserId(int parentUser) {
+ setCurrentUserIds(parentUser, new int[]{parentUser});
+ }
+
public void setCurrentUserIds(int parentUser, int[] currentProfileUserIds) {
Preconditions.checkArgument(ArrayUtils.contains(currentProfileUserIds, parentUser));
int oldUserId = mCurrentUserId;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
index e6f625217965..4165b6ee111f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
@@ -16,7 +16,11 @@
package com.android.server.location.util;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -46,9 +50,7 @@ public class LocationAttributionHelperTest {
public void setUp() {
initMocks(this);
- when(mAppOpsHelper.startLocationMonitoring(any(CallerIdentity.class))).thenReturn(true);
- when(mAppOpsHelper.startHighPowerLocationMonitoring(any(CallerIdentity.class))).thenReturn(
- true);
+ when(mAppOpsHelper.startOpNoThrow(anyInt(), any(CallerIdentity.class))).thenReturn(true);
mHelper = new LocationAttributionHelper(mAppOpsHelper);
}
@@ -63,30 +65,30 @@ public class LocationAttributionHelperTest {
Object key4 = new Object();
mHelper.reportLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
}
@Test
@@ -99,29 +101,29 @@ public class LocationAttributionHelperTest {
Object key4 = new Object();
mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
index f40d3168cf98..093aa2e0e771 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
@@ -20,12 +20,8 @@ import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_MOCK_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -34,10 +30,12 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
+import static org.testng.Assert.assertThrows;
import android.app.AppOpsManager;
import android.content.Context;
@@ -105,41 +103,41 @@ public class SystemAppOpsHelperTest {
}
@Test
- public void testCheckLocationAccess() {
+ public void testCheckOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isTrue();
+ assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isFalse();
+ assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+ assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+ assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
}
@Test
- public void testNoteLocationAccess() {
+ public void testNoteOpNoThrow() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isTrue();
+ assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isFalse();
+ assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
@@ -147,74 +145,55 @@ public class SystemAppOpsHelperTest {
doReturn(MODE_ALLOWED).when(
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+ assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+ assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
}
@Test
- public void testStartLocationMonitoring() {
+ public void testStartOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startLocationMonitoring(identity)).isTrue();
+ assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startLocationMonitoring(identity)).isFalse();
+ assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isFalse();
}
@Test
- public void testStartHighPowerLocationMonitoring() {
+ public void testFinishOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
- doReturn(MODE_ALLOWED).when(
- mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
- eq("mypackage"),
- eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue();
-
- doReturn(MODE_IGNORED).when(
- mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
- eq("mypackage"),
- eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isFalse();
- }
-
- @Test
- public void testStopLocationMonitoring() {
- CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
- mHelper.stopLocationMonitoring(identity);
+ mHelper.finishOp(OP_MONITOR_LOCATION, identity);
verify(mAppOps).finishOp(OP_MONITOR_LOCATION, 1000, "mypackage", "myfeature");
}
@Test
- public void testStopHighPowerLocationMonitoring() {
- CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
- mHelper.stopHighPowerLocationMonitoring(identity);
- verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature");
- }
-
- @Test
- public void testNoteMockLocationAccess() {
+ public void testNoteOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
nullable(String.class));
- assertThat(mHelper.noteMockLocationAccess(identity)).isTrue();
+ assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
nullable(String.class));
- assertThat(mHelper.noteMockLocationAccess(identity)).isFalse();
+ assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isFalse();
+
+
+ doThrow(new SecurityException()).when(
+ mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
+ nullable(String.class));
+ assertThrows(SecurityException.class, () -> mHelper.noteOp(OP_MOCK_LOCATION, identity));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
new file mode 100644
index 000000000000..2acb70c4b83d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.location.util;
+
+import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
+import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
+import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.location.util.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemLocationPowerSaveModeHelperTest {
+
+ private static final long TIMEOUT_MS = 5000;
+ private static final long FAILURE_TIMEOUT_MS = 200;
+
+ @Mock
+ private PowerManagerInternal mPowerManagerInternal;
+
+ private List<Consumer<PowerSaveState>> mListeners = new ArrayList<>();
+
+ private SystemLocationPowerSaveModeHelper mHelper;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+
+ LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternal);
+
+ doAnswer(invocation -> mListeners.add(invocation.getArgument(1))).when(
+ mPowerManagerInternal).registerLowPowerModeObserver(anyInt(), any(Consumer.class));
+
+ PowerManager powerManager = mock(PowerManager.class);
+ doReturn(LOCATION_MODE_NO_CHANGE).when(powerManager).getLocationPowerSaveMode();
+ Context context = mock(Context.class);
+ doReturn(powerManager).when(context).getSystemService(PowerManager.class);
+
+ mHelper = new SystemLocationPowerSaveModeHelper(context);
+ mHelper.onSystemReady();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ }
+
+ private void sendPowerSaveState(PowerSaveState powerSaveState) {
+ for (Consumer<PowerSaveState> listener : mListeners) {
+ listener.accept(powerSaveState);
+ }
+ }
+
+ @Test
+ public void testListener() {
+ LocationPowerSaveModeChangedListener listener = mock(
+ LocationPowerSaveModeChangedListener.class);
+ mHelper.addListener(listener);
+
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+ false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_FOREGROUND_ONLY);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_FOREGROUND_ONLY);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+ true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_NO_CHANGE);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
index c22dc104f438..1867be0b9f3b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
@@ -16,22 +16,32 @@
package com.android.server.location.util;
+import com.android.server.location.LocationRequestStatistics;
+
public class TestInjector implements Injector {
private final FakeUserInfoHelper mUserInfoHelper;
private final FakeAppOpsHelper mAppOpsHelper;
+ private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
private final FakeSettingsHelper mSettingsHelper;
private final FakeAppForegroundHelper mAppForegroundHelper;
- private final LocationUsageLogger mLocationUsageLogger;
+ private final FakeLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+ private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
+ private final LocationUsageLogger mLocationUsageLogger;
+ private final LocationRequestStatistics mLocationRequestStatistics;
public TestInjector() {
mUserInfoHelper = new FakeUserInfoHelper();
mAppOpsHelper = new FakeAppOpsHelper();
+ mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
mSettingsHelper = new FakeSettingsHelper();
mAppForegroundHelper = new FakeAppForegroundHelper();
- mLocationUsageLogger = new LocationUsageLogger();
+ mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper();
+ mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+ mLocationUsageLogger = new LocationUsageLogger();
+ mLocationRequestStatistics = new LocationRequestStatistics();
}
@Override
@@ -45,6 +55,11 @@ public class TestInjector implements Injector {
}
@Override
+ public FakeLocationPermissionsHelper getLocationPermissionsHelper() {
+ return mLocationPermissionsHelper;
+ }
+
+ @Override
public FakeSettingsHelper getSettingsHelper() {
return mSettingsHelper;
}
@@ -55,12 +70,27 @@ public class TestInjector implements Injector {
}
@Override
- public LocationUsageLogger getLocationUsageLogger() {
- return mLocationUsageLogger;
+ public FakeLocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+ return mLocationPowerSaveModeHelper;
+ }
+
+ @Override
+ public FakeScreenInteractiveHelper getScreenInteractiveHelper() {
+ return mScreenInteractiveHelper;
}
@Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
+
+ @Override
+ public LocationUsageLogger getLocationUsageLogger() {
+ return mLocationUsageLogger;
+ }
+
+ @Override
+ public LocationRequestStatistics getLocationRequestStatistics() {
+ return mLocationRequestStatistics;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index daaabf8141ff..9a465a91e84e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -48,8 +48,6 @@ import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -4931,20 +4929,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.thenReturn(passwordMetrics);
dpm.reportPasswordChanged(userHandle);
- // Drain ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED broadcasts as part of
- // reportPasswordChanged()
- // This broadcast should be sent 2-4 times:
- // * Twice from calls to DevicePolicyManagerService.updatePasswordExpirationsLocked,
- // once for each affected user, in DevicePolicyManagerService.reportPasswordChanged.
- // * Optionally, at most twice from calls to DevicePolicyManagerService.saveSettingsLocked
- // in DevicePolicyManagerService.reportPasswordChanged, once with the userId
- // the password change is relevant to and another with the credential owner of said
- // userId, if the password checkpoint value changes.
- verify(mContext.spiedContext, atMost(4)).sendBroadcastAsUser(
- MockUtils.checkIntentAction(
- DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(userHandle));
- verify(mContext.spiedContext, atLeast(2)).sendBroadcastAsUser(
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
MockUtils.checkUserHandle(userHandle));
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 62b6a65cc6cb..614949c91b9a 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -43,11 +43,19 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.content.ClipData;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.net.Uri;
+import android.os.UserHandle;
import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
@@ -62,6 +70,12 @@ public class UriGrantsManagerServiceTest {
private UriGrantsMockContext mContext;
private UriGrantsManagerInternal mService;
+ // we expect the following only during grant if a grant is expected
+ private void verifyNoVisibilityGrant() {
+ verify(mContext.mPmInternal, never())
+ .grantImplicitAccess(anyInt(), any(), anyInt(), anyInt(), anyBoolean());
+ }
+
@Before
public void setUp() throws Exception {
mContext = new UriGrantsMockContext(InstrumentationRegistry.getContext());
@@ -83,6 +97,7 @@ public class UriGrantsManagerServiceTest {
assertEquals(UID_PRIMARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
@@ -100,6 +115,7 @@ public class UriGrantsManagerServiceTest {
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
@@ -111,6 +127,8 @@ public class UriGrantsManagerServiceTest {
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
intent, UID_PRIMARY_PUBLIC, PKG_SOCIAL, USER_PRIMARY);
assertNull(needed);
+ verify(mContext.mPmInternal).grantImplicitAccess(eq(USER_PRIMARY), isNull(), eq(
+ UserHandle.getAppId(UID_PRIMARY_SOCIAL)), eq(UID_PRIMARY_PUBLIC), eq(false));
}
/**
@@ -128,6 +146,7 @@ public class UriGrantsManagerServiceTest {
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 32686538c10d..ff54fccda767 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -75,8 +75,10 @@ struct ResourcePathData {
};
// Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, const char dir_sep,
- std::string* out_error) {
+static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
+ const char dir_sep,
+ std::string* out_error,
+ const CompileOptions& options) {
std::vector<std::string> parts = util::Split(path, dir_sep);
if (parts.size() < 2) {
if (out_error) *out_error = "bad resource path";
@@ -121,7 +123,11 @@ static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
}
}
- return ResourcePathData{Source(path), dir_str.to_string(), name.to_string(),
+ const Source res_path = options.source_path
+ ? StringPiece(options.source_path.value())
+ : StringPiece(path);
+
+ return ResourcePathData{res_path, dir_str.to_string(), name.to_string(),
extension.to_string(), config_str.to_string(), config};
}
@@ -667,7 +673,8 @@ int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter*
// Extract resource type information from the full path
std::string err_str;
ResourcePathData path_data;
- if (auto maybe_path_data = ExtractResourcePathData(path, inputs->GetDirSeparator(), &err_str)) {
+ if (auto maybe_path_data = ExtractResourcePathData(
+ path, inputs->GetDirSeparator(), &err_str, options)) {
path_data = maybe_path_data.value();
} else {
context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << err_str);
@@ -747,6 +754,11 @@ int CompileCommand::Action(const std::vector<std::string>& args) {
context.GetDiagnostics()->Error(DiagMessage()
<< "only one of --dir and --zip can be specified");
return 1;
+ } else if ((options_.res_dir || options_.res_zip) &&
+ options_.source_path && args.size() > 1) {
+ context.GetDiagnostics()->Error(DiagMessage(kPath)
+ << "Cannot use an overriding source path with multiple files.");
+ return 1;
} else if (options_.res_dir) {
if (!args.empty()) {
context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 1752a1adac24..1bc1f6651f85 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -28,6 +28,7 @@ namespace aapt {
struct CompileOptions {
std::string output_path;
+ Maybe<std::string> source_path;
Maybe<std::string> res_dir;
Maybe<std::string> res_zip;
Maybe<std::string> generate_text_symbols_path;
@@ -69,6 +70,9 @@ class CompileCommand : public Command {
AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
AddOptionalFlag("--trace-folder", "Generate systrace json trace fragment to specified folder.",
&trace_folder_);
+ AddOptionalFlag("--source-path",
+ "Sets the compiled resource file source file path to the given string.",
+ &options_.source_path);
}
int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index fb786a31360e..0aab94d3299f 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -24,6 +24,7 @@
#include "io/ZipArchive.h"
#include "java/AnnotationProcessor.h"
#include "test/Test.h"
+#include "format/proto/ProtoDeserialize.h"
namespace aapt {
@@ -253,4 +254,90 @@ TEST_F(CompilerTest, DoNotTranslateTest) {
AssertTranslations(this, "donottranslate_foo", expected_not_translatable);
}
+TEST_F(CompilerTest, RelativePathTest) {
+ StdErrDiagnostics diag;
+ const std::string res_path = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "res"});
+
+ const std::string path_values_colors = GetTestPath("values/colors.xml");
+ WriteFile(path_values_colors, "<resources>"
+ "<color name=\"color_one\">#008577</color>"
+ "</resources>");
+
+ const std::string path_layout_layout_one = GetTestPath("layout/layout_one.xml");
+ WriteFile(path_layout_layout_one, "<LinearLayout "
+ "xmlns:android=\"http://schemas.android.com/apk/res/android\">"
+ "<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
+ "</LinearLayout>");
+
+ const std::string compiled_files_dir = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "compiled"});
+ CHECK(file::mkdirs(compiled_files_dir.data()));
+
+ const std::string path_values_colors_out =
+ BuildPath({compiled_files_dir,"values_colors.arsc.flat"});
+ const std::string path_layout_layout_one_out =
+ BuildPath({compiled_files_dir, "layout_layout_one.flat"});
+ ::android::base::utf8::unlink(path_values_colors_out.c_str());
+ ::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
+ const std::string apk_path = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "out.apk"});
+
+ const std::string source_set_res = BuildPath({"main", "res"});
+ const std::string relative_path_values_colors =
+ BuildPath({source_set_res, "values", "colors.xml"});
+ const std::string relative_path_layout_layout_one =
+ BuildPath({source_set_res, "layout", "layout_one.xml"});
+
+ CompileCommand(&diag).Execute({
+ path_values_colors,
+ "-o",
+ compiled_files_dir,
+ "--source-path",
+ relative_path_values_colors},
+ &std::cerr);
+
+ CompileCommand(&diag).Execute({
+ path_layout_layout_one,
+ "-o",
+ compiled_files_dir,
+ "--source-path",
+ relative_path_layout_layout_one},
+ &std::cerr);
+
+ std::ifstream ifs_values(path_values_colors_out);
+ std::string content_values((std::istreambuf_iterator<char>(ifs_values)),
+ (std::istreambuf_iterator<char>()));
+ ASSERT_NE(content_values.find(relative_path_values_colors), -1);
+ ASSERT_EQ(content_values.find(path_values_colors), -1);
+
+ Link({"-o", apk_path, "--manifest", GetDefaultManifest(), "--proto-format"},
+ compiled_files_dir, &diag);
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, &diag);
+ ResourceTable* resource_table = apk.get()->GetResourceTable();
+ const std::vector<std::unique_ptr<StringPool::Entry>>& pool_strings =
+ resource_table->string_pool.strings();
+
+ ASSERT_EQ(pool_strings.size(), 2);
+ ASSERT_EQ(pool_strings[0]->value, "res/layout/layout_one.xml");
+ ASSERT_EQ(pool_strings[1]->value, "res/layout-v1/layout_one.xml");
+
+ // Check resources.pb contains relative sources.
+ io::IFile* proto_file =
+ apk.get()->GetFileCollection()->FindFile("resources.pb");
+ std::unique_ptr<io::InputStream> proto_stream = proto_file->OpenInputStream();
+ io::ProtoInputStreamReader proto_reader(proto_stream.get());
+ pb::ResourceTable pb_table;
+ proto_reader.ReadMessage(&pb_table);
+
+ const std::string pool_strings_proto = pb_table.source_pool().data();
+
+ ASSERT_NE(pool_strings_proto.find(relative_path_values_colors), -1);
+ ASSERT_NE(pool_strings_proto.find(relative_path_layout_layout_one), -1);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 4a6bfd031284..53d9ffe21949 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1405,6 +1405,29 @@ class UsesStaticLibrary : public ManifestExtractor::Element {
}
};
+/** Represents <uses-native-library> elements. **/
+class UsesNativeLibrary : public ManifestExtractor::Element {
+ public:
+ UsesNativeLibrary() = default;
+ std::string name;
+ int required;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ if (!name.empty()) {
+ printer->Print(StringPrintf("uses-native-library%s:'%s'\n",
+ (required == 0) ? "-not-required" : "", name.data()));
+ }
+ }
+};
+
/**
* Represents <meta-data> elements. These tags are only printed when a flag is passed in to
* explicitly enable meta data printing.
@@ -2245,6 +2268,7 @@ T* ElementCast(ManifestExtractor::Element* element) {
{"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
{"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
};
auto check = kTagCheck.find(element->tag());
@@ -2295,6 +2319,7 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
{"uses-package", &CreateType<UsesPackage>},
{"additional-certificate", &CreateType<AdditionalCertificate>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
+ {"uses-native-library", &CreateType<UsesNativeLibrary>},
};
// Attempt to map the xml tag to a element inflater
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 3d69093a936a..49f8e1bcd30b 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -422,6 +422,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
application_action.Action(OptionalNameIsJavaClassName);
application_action["uses-library"].Action(RequiredNameIsNotEmpty);
+ application_action["uses-native-library"].Action(RequiredNameIsNotEmpty);
application_action["library"].Action(RequiredNameIsNotEmpty);
application_action["profileable"];