summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/AppOpsManager.java50
-rw-r--r--core/java/android/app/LoadedApk.java4
-rw-r--r--core/java/android/net/vcn/VcnGatewayConnectionConfig.java2
-rw-r--r--core/java/android/permission/PermissionManager.java35
-rw-r--r--core/java/android/permission/PermissionUsageHelper.java4
-rw-r--r--core/java/android/view/translation/UiTranslationController.java2
-rw-r--r--core/java/android/widget/TextView.java31
-rw-r--r--core/java/android/widget/TextViewTranslationCallback.java69
-rw-r--r--core/java/com/android/internal/os/BatterySipper.java3
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHelper.java3
-rw-r--r--core/java/com/android/internal/util/function/DodecConsumer.java29
-rw-r--r--core/java/com/android/internal/util/function/DodecFunction.java28
-rw-r--r--core/java/com/android/internal/util/function/DodecPredicate.java28
-rwxr-xr-xcore/java/com/android/internal/util/function/pooled/PooledLambda.java229
-rwxr-xr-xcore/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java30
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java5
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml4
-rw-r--r--libs/hwui/RenderNode.h2
-rw-r--r--libs/hwui/effects/StretchEffect.cpp7
-rw-r--r--libs/hwui/effects/StretchEffect.h4
-rw-r--r--location/java/android/location/ILocationManager.aidl3
-rw-r--r--location/java/android/location/LastLocationRequest.java60
-rw-r--r--location/java/android/location/LocationManager.java63
-rw-r--r--location/java/android/location/LocationRequest.java72
-rw-r--r--location/java/android/location/provider/ProviderRequest.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java7
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java20
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java10
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java25
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteFillService.java5
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java63
-rw-r--r--services/core/java/com/android/server/app/GameManagerShellCommand.java72
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java39
-rw-r--r--services/core/java/com/android/server/appop/DiscreteRegistry.java287
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java10
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java187
-rw-r--r--services/core/java/com/android/server/location/LocationShellCommand.java70
-rw-r--r--services/core/java/com/android/server/location/eventlog/LocationEventLog.java52
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java4
-rw-r--r--services/core/java/com/android/server/location/injector/Injector.java4
-rw-r--r--services/core/java/com/android/server/location/injector/SystemSettingsHelper.java2
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java91
-rw-r--r--services/core/java/com/android/server/location/settings/LocationSettings.java173
-rw-r--r--services/core/java/com/android/server/location/settings/LocationUserSettings.java98
-rw-r--r--services/core/java/com/android/server/location/settings/SettingsStore.java166
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java26
-rw-r--r--services/core/java/com/android/server/vibrator/StepToRampAdapter.java76
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java33
-rw-r--r--services/core/java/com/android/server/wm/CompatModePackages.java112
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java7
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java13
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java110
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java54
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java171
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java32
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java64
74 files changed, 2699 insertions, 423 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 96d59b80b479..9bd6c750fb13 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4804,6 +4804,16 @@ public class AppOpsManager {
public static final int HISTORY_FLAG_DISCRETE = 1 << 1;
/**
+ * Flag for querying app op history: assemble attribution chains, and attach the last visible
+ * node in the chain to the start as a proxy info. This only applies to discrete accesses.
+ *
+ * TODO 191512294: Add to @SystemApi
+ *
+ * @hide
+ */
+ public static final int HISTORY_FLAG_GET_ATTRIBUTION_CHAINS = 1 << 2;
+
+ /**
* Flag for querying app op history: get all types of historical access information.
*
* @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer)
@@ -4819,7 +4829,8 @@ public class AppOpsManager {
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "HISTORY_FLAG_" }, value = {
HISTORY_FLAG_AGGREGATE,
- HISTORY_FLAG_DISCRETE
+ HISTORY_FLAG_DISCRETE,
+ HISTORY_FLAG_GET_ATTRIBUTION_CHAINS
})
public @interface OpHistoryFlags {}
@@ -5037,7 +5048,8 @@ public class AppOpsManager {
* @return This builder.
*/
public @NonNull Builder setHistoryFlags(@OpHistoryFlags int flags) {
- Preconditions.checkFlagsArgument(flags, HISTORY_FLAGS_ALL);
+ Preconditions.checkFlagsArgument(flags,
+ HISTORY_FLAGS_ALL | HISTORY_FLAG_GET_ATTRIBUTION_CHAINS);
mHistoryFlags = flags;
return this;
}
@@ -5290,8 +5302,17 @@ public class AppOpsManager {
@Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
long discreteAccessTime, long discreteAccessDuration) {
getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
- uidState, opFlag, discreteAccessTime, discreteAccessDuration);
- };
+ uidState, opFlag, discreteAccessTime, discreteAccessDuration, null);
+ }
+
+ /** @hide */
+ public void addDiscreteAccess(int opCode, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
+ long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
+ getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
+ uidState, opFlag, discreteAccessTime, discreteAccessDuration, proxy);
+ }
/** @hide */
@@ -5623,9 +5644,10 @@ public class AppOpsManager {
private void addDiscreteAccess(int opCode, @NonNull String packageName,
@Nullable String attributionTag, @UidState int uidState,
- @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration) {
+ @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
getOrCreateHistoricalPackageOps(packageName).addDiscreteAccess(opCode, attributionTag,
- uidState, flag, discreteAccessTime, discreteAccessDuration);
+ uidState, flag, discreteAccessTime, discreteAccessDuration, proxy);
};
/**
@@ -5889,9 +5911,9 @@ public class AppOpsManager {
private void addDiscreteAccess(int opCode, @Nullable String attributionTag,
@UidState int uidState, @OpFlags int flag, long discreteAccessTime,
- long discreteAccessDuration) {
+ long discreteAccessDuration, @Nullable OpEventProxyInfo proxy) {
getOrCreateAttributedHistoricalOps(attributionTag).addDiscreteAccess(opCode, uidState,
- flag, discreteAccessTime, discreteAccessDuration);
+ flag, discreteAccessTime, discreteAccessDuration, proxy);
}
/**
@@ -6212,9 +6234,10 @@ public class AppOpsManager {
}
private void addDiscreteAccess(int opCode, @UidState int uidState, @OpFlags int flag,
- long discreteAccessTime, long discreteAccessDuration) {
+ long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
getOrCreateHistoricalOp(opCode).addDiscreteAccess(uidState,flag, discreteAccessTime,
- discreteAccessDuration);
+ discreteAccessDuration, proxy);
}
/**
@@ -6583,11 +6606,12 @@ public class AppOpsManager {
}
private void addDiscreteAccess(@UidState int uidState, @OpFlags int flag,
- long discreteAccessTime, long discreteAccessDuration) {
+ long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
List<AttributedOpEntry> discreteAccesses = getOrCreateDiscreteAccesses();
LongSparseArray<NoteOpEvent> accessEvents = new LongSparseArray<>();
long key = makeKey(uidState, flag);
- NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, null);
+ NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, proxy);
accessEvents.append(key, note);
AttributedOpEntry access = new AttributedOpEntry(mOp, false, accessEvents, null);
int insertionPoint = discreteAccesses.size() - 1;
@@ -10022,6 +10046,8 @@ public class AppOpsManager {
NoteOpEvent existingAccess = accessEvents.get(key);
if (existingAccess == null || existingAccess.getDuration() == -1) {
accessEvents.append(key, access);
+ } else if (existingAccess.mProxy == null && access.mProxy != null ) {
+ existingAccess.mProxy = access.mProxy;
}
}
if (reject != null) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 9ed76c1c13d1..a2c9795204ad 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -546,6 +546,10 @@ public final class LoadedApk {
if (aInfo.sharedLibraryFiles != null) {
int index = 0;
for (String lib : aInfo.sharedLibraryFiles) {
+ // sharedLibraryFiles might contain native shared libraries that are not APK paths.
+ if (!lib.endsWith(".apk")) {
+ continue;
+ }
if (!outSeenPaths.contains(lib) && !outZipPaths.contains(lib)) {
outZipPaths.add(index, lib);
index++;
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 86cd23d1e942..752ef3e39ab6 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -352,6 +352,7 @@ public final class VcnGatewayConnectionConfig {
public int hashCode() {
return Objects.hash(
mGatewayConnectionName,
+ mTunnelConnectionParams,
mExposedCapabilities,
Arrays.hashCode(mRetryIntervalsMs),
mMaxMtu);
@@ -365,6 +366,7 @@ public final class VcnGatewayConnectionConfig {
final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other;
return mGatewayConnectionName.equals(rhs.mGatewayConnectionName)
+ && mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams)
&& mExposedCapabilities.equals(rhs.mExposedCapabilities)
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
&& mMaxMtu == rhs.mMaxMtu;
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index c5d0cd40bc6d..4ef0e6e785e8 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -908,9 +908,32 @@ public final class PermissionManager {
*/
public static boolean shouldShowPackageForIndicatorCached(@NonNull Context context,
@NonNull String packageName) {
- if (SYSTEM_PKG.equals(packageName)) {
- return false;
+ return !getIndicatorExemptedPackages(context).contains(packageName);
+ }
+
+ /**
+ * Get the list of packages that are not shown by the indicators. Only a select few roles, and
+ * the system app itself, are hidden. These values are updated at most every 15 seconds.
+ * @hide
+ */
+ public static Set<String> getIndicatorExemptedPackages(@NonNull Context context) {
+ updateIndicatorExemptedPackages(context);
+ ArraySet<String> pkgNames = new ArraySet<>();
+ pkgNames.add(SYSTEM_PKG);
+ for (int i = 0; i < INDICATOR_EXEMPTED_PACKAGES.length; i++) {
+ String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
+ if (exemptedPackage != null) {
+ pkgNames.add(exemptedPackage);
+ }
}
+ return pkgNames;
+ }
+
+ /**
+ * Update the cached indicator exempted packages
+ * @hide
+ */
+ public static void updateIndicatorExemptedPackages(@NonNull Context context) {
long now = SystemClock.elapsedRealtime();
if (sLastIndicatorUpdateTime == -1
|| (now - sLastIndicatorUpdateTime) > EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS) {
@@ -919,14 +942,6 @@ public final class PermissionManager {
INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]);
}
}
- for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
- String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
- if (exemptedPackage != null && exemptedPackage.equals(packageName)) {
- return false;
- }
- }
-
- return true;
}
/**
* Gets the list of packages that have permissions that specified
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index d4e548e1df1e..791764b4342f 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -410,7 +410,9 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
int usageAttr = usage.getPackageIdHash();
// If this usage has a proxy, but is not a proxy, it is the end of a chain.
- if (!proxies.containsKey(usageAttr) && usage.proxy != null) {
+ // TODO remove once camera converted
+ if (!proxies.containsKey(usageAttr) && usage.proxy != null
+ && !usage.op.equals(OPSTR_RECORD_AUDIO)) {
proxyLabels.put(usage, new ArrayList<>());
proxyPackages.add(usage.getPackageIdHash());
}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 5ac878d88100..592993cc3d3e 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -424,7 +424,7 @@ public class UiTranslationController {
if (callback == null) {
if (view instanceof TextView) {
// developer doesn't provide their override, we set the default TextView
- // implememtation.
+ // implementation.
callback = new TextViewTranslationCallback();
view.setViewTranslationCallback(callback);
} else {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cd560d75d913..83fd7b49c2bd 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -129,7 +129,6 @@ import android.text.method.TextKeyListener;
import android.text.method.TimeKeyListener;
import android.text.method.TransformationMethod;
import android.text.method.TransformationMethod2;
-import android.text.method.TranslationTransformationMethod;
import android.text.method.WordIterator;
import android.text.style.CharacterStyle;
import android.text.style.ClickableSpan;
@@ -199,7 +198,6 @@ import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationController;
import android.view.translation.ViewTranslationCallback;
import android.view.translation.ViewTranslationRequest;
-import android.view.translation.ViewTranslationResponse;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.annotations.VisibleForTesting;
@@ -13946,33 +13944,4 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
requestsCollector.accept(requestBuilder.build());
}
-
- /**
- *
- * Called when the content from {@link #onCreateViewTranslationRequest} had been translated by
- * the TranslationService. The default implementation will replace the current
- * {@link TransformationMethod} to transform the original text to the translated text display.
- *
- * @param response a {@link ViewTranslationResponse} that contains the translated information
- * which can be shown in the view.
- */
- @Override
- public void onViewTranslationResponse(@NonNull ViewTranslationResponse response) {
- // set ViewTranslationResponse
- super.onViewTranslationResponse(response);
- // TODO(b/178353965): move to ViewTranslationCallback.onShow()
- ViewTranslationCallback callback = getViewTranslationCallback();
- if (callback instanceof TextViewTranslationCallback) {
- TextViewTranslationCallback textViewDefaultCallback =
- (TextViewTranslationCallback) callback;
- TranslationTransformationMethod oldTranslationMethod =
- textViewDefaultCallback.getTranslationTransformation();
- TransformationMethod originalTranslationMethod = oldTranslationMethod != null
- ? oldTranslationMethod.getOriginalTransformationMethod() : mTransformation;
- TranslationTransformationMethod newTranslationMethod =
- new TranslationTransformationMethod(response, originalTranslationMethod);
- // TODO(b/178353965): well-handle setTransformationMethod.
- textViewDefaultCallback.setTranslationTransformation(newTranslationMethod);
- }
- }
}
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index a7d5ee465299..e1b04f8957e5 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -56,26 +56,6 @@ public class TextViewTranslationCallback implements ViewTranslationCallback {
private CharSequence mContentDescription;
- /**
- * Invoked by the platform when receiving the successful {@link ViewTranslationResponse} for the
- * view that provides the translatable information by {@link View#createTranslationRequest} and
- * sent by the platform.
- */
- void setTranslationTransformation(TranslationTransformationMethod method) {
- if (method == null) {
- if (DEBUG) {
- Log.w(TAG, "setTranslationTransformation: should not set null "
- + "TranslationTransformationMethod");
- }
- return;
- }
- mTranslationTransformation = method;
- }
-
- TranslationTransformationMethod getTranslationTransformation() {
- return mTranslationTransformation;
- }
-
private void clearTranslationTransformation() {
if (DEBUG) {
Log.v(TAG, "clearTranslationTransformation: " + mTranslationTransformation);
@@ -88,34 +68,33 @@ public class TextViewTranslationCallback implements ViewTranslationCallback {
*/
@Override
public boolean onShowTranslation(@NonNull View view) {
- if (view.getViewTranslationResponse() == null) {
- Log.wtf(TAG, "onShowTranslation() shouldn't be called before "
+ ViewTranslationResponse response = view.getViewTranslationResponse();
+ if (response == null) {
+ Log.e(TAG, "onShowTranslation() shouldn't be called before "
+ "onViewTranslationResponse().");
return false;
}
- if (mTranslationTransformation != null) {
- final TransformationMethod transformation = mTranslationTransformation;
- runWithAnimation(
- (TextView) view,
- () -> {
- mIsShowingTranslation = true;
- ((TextView) view).setTransformationMethod(transformation);
- });
- ViewTranslationResponse response = view.getViewTranslationResponse();
- if (response.getKeys().contains(ViewTranslationRequest.ID_CONTENT_DESCRIPTION)) {
- CharSequence translatedContentDescription =
- response.getValue(ViewTranslationRequest.ID_CONTENT_DESCRIPTION).getText();
- if (!TextUtils.isEmpty(translatedContentDescription)) {
- mContentDescription = view.getContentDescription();
- view.setContentDescription(translatedContentDescription);
- }
- }
- } else {
- if (DEBUG) {
- // TODO(b/182433547): remove before S release
- Log.w(TAG, "onShowTranslation(): no translated text.");
+ if (mTranslationTransformation == null) {
+ TransformationMethod originalTranslationMethod =
+ ((TextView) view).getTransformationMethod();
+ mTranslationTransformation = new TranslationTransformationMethod(response,
+ originalTranslationMethod);
+ }
+ final TransformationMethod transformation = mTranslationTransformation;
+ runWithAnimation(
+ (TextView) view,
+ () -> {
+ mIsShowingTranslation = true;
+ // TODO(b/178353965): well-handle setTransformationMethod.
+ ((TextView) view).setTransformationMethod(transformation);
+ });
+ if (response.getKeys().contains(ViewTranslationRequest.ID_CONTENT_DESCRIPTION)) {
+ CharSequence translatedContentDescription =
+ response.getValue(ViewTranslationRequest.ID_CONTENT_DESCRIPTION).getText();
+ if (!TextUtils.isEmpty(translatedContentDescription)) {
+ mContentDescription = view.getContentDescription();
+ view.setContentDescription(translatedContentDescription);
}
- return false;
}
return true;
}
@@ -126,7 +105,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback {
@Override
public boolean onHideTranslation(@NonNull View view) {
if (view.getViewTranslationResponse() == null) {
- Log.wtf(TAG, "onHideTranslation() shouldn't be called before "
+ Log.e(TAG, "onHideTranslation() shouldn't be called before "
+ "onViewTranslationResponse().");
return false;
}
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 4f2f973b51b1..dfd561a8cc30 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -23,7 +23,10 @@ import java.util.List;
/**
* Contains power usage of an application, system service, or hardware type.
+ *
+ * @deprecated Please use BatteryStatsManager.getBatteryUsageStats instead.
*/
+@Deprecated
public class BatterySipper implements Comparable<BatterySipper> {
@UnsupportedAppUsage
public int userId;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index b20f50d62de4..608782a39f6b 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -59,7 +59,10 @@ import java.util.List;
*
* The caller must initialize this class as soon as activity object is ready to use (for example, in
* onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
+ *
+ * @deprecated Please use BatteryStatsManager.getBatteryUsageStats instead.
*/
+@Deprecated
public class BatteryStatsHelper {
static final boolean DEBUG = false;
diff --git a/core/java/com/android/internal/util/function/DodecConsumer.java b/core/java/com/android/internal/util/function/DodecConsumer.java
new file mode 100644
index 000000000000..b4d2fb94d245
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecConsumer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 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.internal.util.function;
+
+import java.util.function.Consumer;
+
+
+/**
+ * A 12-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface DodecConsumer<A, B, C, D, E, F, G, H, I, J, K, L> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/DodecFunction.java b/core/java/com/android/internal/util/function/DodecFunction.java
new file mode 100644
index 000000000000..178b2c111fd7
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 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.internal.util.function;
+
+import java.util.function.Function;
+
+/**
+ * A 12-argument {@link Function}
+ *
+ * @hide
+ */
+public interface DodecFunction<A, B, C, D, E, F, G, H, I, J, K, L, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/DodecPredicate.java b/core/java/com/android/internal/util/function/DodecPredicate.java
new file mode 100644
index 000000000000..d3a2b856100f
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecPredicate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 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.internal.util.function;
+
+import java.util.function.Predicate;
+
+/**
+ * A 12-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface DodecPredicate<A, B, C, D, E, F, G, H, I, J, K, L> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index a60cc0fb101f..f073c1c046c5 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -23,6 +23,8 @@ import android.os.Message;
import com.android.internal.util.function.DecConsumer;
import com.android.internal.util.function.DecFunction;
+import com.android.internal.util.function.DodecConsumer;
+import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HeptConsumer;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
@@ -188,7 +190,7 @@ public interface PooledLambda {
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -205,7 +207,7 @@ public interface PooledLambda {
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -222,7 +224,7 @@ public interface PooledLambda {
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -253,7 +255,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -273,7 +275,7 @@ public interface PooledLambda {
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -291,7 +293,7 @@ public interface PooledLambda {
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -309,7 +311,7 @@ public interface PooledLambda {
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -327,7 +329,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -345,7 +347,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -364,7 +366,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -384,7 +386,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -405,7 +407,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -423,7 +425,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -441,7 +443,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -459,7 +461,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -477,7 +479,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -509,7 +511,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -530,7 +532,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -549,7 +551,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -568,7 +570,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -587,7 +589,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -606,7 +608,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -625,7 +627,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -644,7 +646,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -663,7 +665,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -696,7 +698,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -718,7 +720,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -738,7 +740,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -758,7 +760,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -778,7 +780,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -798,7 +800,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -818,7 +820,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -838,7 +840,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -858,7 +860,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -878,7 +880,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -898,7 +900,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -932,7 +934,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -955,7 +957,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -976,7 +978,7 @@ public interface PooledLambda {
function, A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1012,7 +1014,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1036,7 +1038,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1058,7 +1060,7 @@ public interface PooledLambda {
? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1095,7 +1097,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1120,7 +1122,7 @@ public interface PooledLambda {
? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1144,7 +1146,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1182,7 +1184,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1209,7 +1211,7 @@ public interface PooledLambda {
H arg8) {
return acquire(PooledLambdaImpl.sPool,
function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1234,7 +1236,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) {
return acquire(PooledLambdaImpl.sPool,
function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1274,7 +1276,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1302,7 +1304,7 @@ public interface PooledLambda {
E arg5, F arg6, G arg7, H arg8, I arg9) {
return acquire(PooledLambdaImpl.sPool,
function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, null, null);
+ arg9, null, null, null);
}
/**
@@ -1328,7 +1330,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) {
return acquire(PooledLambdaImpl.sPool,
function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, null, null);
+ arg9, null, null, null);
}
/**
@@ -1369,7 +1371,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, null, null);
+ arg9, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1398,7 +1400,7 @@ public interface PooledLambda {
D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
return acquire(PooledLambdaImpl.sPool,
function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, null);
+ arg9, arg10, null, null);
}
/**
@@ -1425,7 +1427,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
return acquire(PooledLambdaImpl.sPool,
function, 10, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, null);
+ arg9, arg10, null, null);
}
/**
@@ -1467,7 +1469,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
- arg8, arg9, arg10, null);
+ arg8, arg9, arg10, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1498,7 +1500,7 @@ public interface PooledLambda {
C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10, K arg11) {
return acquire(PooledLambdaImpl.sPool,
function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, arg11);
+ arg9, arg10, arg11, null);
}
/**
@@ -1528,7 +1530,7 @@ public interface PooledLambda {
K arg11) {
return acquire(PooledLambdaImpl.sPool,
function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, arg11);
+ arg9, arg10, arg11, null);
}
/**
@@ -1571,7 +1573,118 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
- arg8, arg9, arg10, arg11);
+ arg8, arg9, arg10, arg11, null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @param arg12 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ * arg11, arg12) }
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K, L> PooledRunnable obtainRunnable(
+ DodecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K,
+ ? super L> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+ K arg11, L arg12) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 12, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, arg11, arg12);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @param arg12 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ * arg11) }
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K, L, R> PooledSupplier<R> obtainSupplier(
+ DodecFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K, ? extends L,
+ ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+ K arg11, L arg12) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, arg11, arg12);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @param arg12 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8, arg9, arg10, arg11) } when handled
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K, L> Message obtainMessage(
+ DodecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K, ? super L> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+ K arg11, L arg12) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ arg8, arg9, arg10, arg11, arg12);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 1646a07b8001..19f0816e3e48 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -28,6 +28,9 @@ import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.function.DecConsumer;
import com.android.internal.util.function.DecFunction;
import com.android.internal.util.function.DecPredicate;
+import com.android.internal.util.function.DodecConsumer;
+import com.android.internal.util.function.DodecFunction;
+import com.android.internal.util.function.DodecPredicate;
import com.android.internal.util.function.HeptConsumer;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HeptPredicate;
@@ -458,6 +461,28 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
}
}
} break;
+
+ case 12: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((DodecConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), popArg(11));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((DodecPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7), popArg(8), popArg(9), popArg(10),
+ popArg(11));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((DodecFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), popArg(11));
+ }
+ }
+ } break;
}
throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
}
@@ -523,7 +548,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
*/
static <E extends PooledLambda> E acquire(Pool pool, Object func,
int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c,
- Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k) {
+ Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k,
+ Object l) {
PooledLambdaImpl r = acquire(pool);
if (DEBUG) {
Log.i(LOG_TAG,
@@ -543,6 +569,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
+ ", i = " + i
+ ", j = " + j
+ ", k = " + k
+ + ", l = " + l
+ ")");
}
r.mFunc = Objects.requireNonNull(func);
@@ -560,6 +587,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
setIfInBounds(r.mArgs, 8, i);
setIfInBounds(r.mArgs, 9, j);
setIfInBounds(r.mArgs, 10, k);
+ setIfInBounds(r.mArgs, 11, l);
return (E) r;
}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 73c5460755b1..3f756d757706 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1524,7 +1524,9 @@ public class LockPatternView extends View {
if (virtualViewId != ExploreByTouchHelper.INVALID_ID) {
int row = (virtualViewId - VIRTUAL_BASE_VIEW_ID) / 3;
int col = (virtualViewId - VIRTUAL_BASE_VIEW_ID) % 3;
- return !mPatternDrawLookup[row][col];
+ if (row < 3) {
+ return !mPatternDrawLookup[row][col];
+ }
}
return false;
}
@@ -1570,7 +1572,6 @@ public class LockPatternView extends View {
final Rect bounds = mTempRect;
final int row = ordinal / 3;
final int col = ordinal % 3;
- final CellState cell = mCellStates[row][col];
float centerX = getCenterXForColumn(col);
float centerY = getCenterYForRow(row);
float cellheight = mSquareHeight * mHitFactor * 0.5f;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9b92306be6fa..7c7a89385c5e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1706,6 +1706,10 @@
config_enableFusedLocationOverlay is false. -->
<string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string>
+ <!-- Default value for the ADAS GNSS Location Enabled setting if this setting has never been
+ set before. -->
+ <bool name="config_defaultAdasGnssLocationEnabled" translatable="false">false</bool>
+
<string-array name="config_locationExtraPackageNames" translatable="false"></string-array>
<!-- The package name of the default network recommendation app.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e51cfcefad0a..94c65a04e244 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1918,6 +1918,7 @@
<java-symbol type="bool" name="config_tintNotificationActionButtons" />
<java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
<java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
+ <java-symbol type="bool" name="config_defaultAdasGnssLocationEnabled" />
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
<java-symbol type="bool" name="config_enableGeocoderOverlay" />
<java-symbol type="bool" name="config_enableGeofenceOverlay" />
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 70f03d234a56..f28ee820eb35 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -117,7 +117,9 @@
<!-- This should be at least the size of bubble_expanded_view_padding; it is used to include
a slight touch slop around the expanded view. -->
<dimen name="bubble_expanded_view_slop">8dp</dimen>
- <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
+ <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded.
+ If this value changes then R.dimen.bubble_expanded_view_min_height in CtsVerifier
+ should also be updated. -->
<dimen name="bubble_expanded_default_height">180dp</dimen>
<!-- On large screens the width of the expanded view is restricted to this size. -->
<dimen name="bubble_expanded_view_tablet_width">412dp</dimen>
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index c770150650e2..45a4f6c9c70d 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -331,6 +331,8 @@ public:
mSkiaLayer.reset();
}
+ mProperties.mutateLayerProperties().mutableStretchEffect().clear();
+ mStretchMask.clear();
// Clear out the previous snapshot and the image filter the previous
// snapshot was created with whenever the layer changes.
mSnapshotResult.snapshot = nullptr;
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index 43f805d906a5..17cd3ceb577c 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -186,6 +186,7 @@ static const SkString stretchShader = SkString(R"(
static const float ZERO = 0.f;
static const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
+static const char CONTENT_TEXTURE[] = "uContentTexture";
sk_sp<SkShader> StretchEffect::getShader(float width, float height,
const sk_sp<SkImage>& snapshotImage,
@@ -207,7 +208,7 @@ sk_sp<SkShader> StretchEffect::getShader(float width, float height,
mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
}
- mBuilder->child("uContentTexture") =
+ mBuilder->child(CONTENT_TEXTURE) =
snapshotImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
SkSamplingOptions(SkFilterMode::kLinear), matrix);
mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
@@ -226,7 +227,9 @@ sk_sp<SkShader> StretchEffect::getShader(float width, float height,
mBuilder->uniform("viewportWidth").set(&width, 1);
mBuilder->uniform("viewportHeight").set(&height, 1);
- return mBuilder->makeShader(nullptr, false);
+ auto result = mBuilder->makeShader(nullptr, false);
+ mBuilder->child(CONTENT_TEXTURE) = nullptr;
+ return result;
}
sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 25777c278a11..3eab9f05ebe5 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -113,6 +113,10 @@ public:
return !isEmpty();
}
+ void clear() {
+ mBuilder = nullptr;
+ }
+
private:
static sk_sp<SkRuntimeEffect> getStretchEffect();
mutable SkVector mStretchDirection{0, 0};
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index c9e4e0a9cb92..5d5c0fc6265d 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -122,6 +122,9 @@ interface ILocationManager
boolean isLocationEnabledForUser(int userId);
void setLocationEnabledForUser(boolean enabled, int userId);
+ boolean isAdasGnssLocationEnabledForUser(int userId);
+ void setAdasGnssLocationEnabledForUser(boolean enabled, int userId);
+
void addTestProvider(String name, in ProviderProperties properties,
in List<String> locationTags, String packageName, @nullable String attributionTag);
void removeTestProvider(String provider, String packageName, @nullable String attributionTag);
diff --git a/location/java/android/location/LastLocationRequest.java b/location/java/android/location/LastLocationRequest.java
index 9ea8048ad476..0970c1c76a36 100644
--- a/location/java/android/location/LastLocationRequest.java
+++ b/location/java/android/location/LastLocationRequest.java
@@ -34,12 +34,15 @@ import java.util.Objects;
public final class LastLocationRequest implements Parcelable {
private final boolean mHiddenFromAppOps;
+ private final boolean mAdasGnssBypass;
private final boolean mLocationSettingsIgnored;
private LastLocationRequest(
boolean hiddenFromAppOps,
+ boolean adasGnssBypass,
boolean locationSettingsIgnored) {
mHiddenFromAppOps = hiddenFromAppOps;
+ mAdasGnssBypass = adasGnssBypass;
mLocationSettingsIgnored = locationSettingsIgnored;
}
@@ -56,6 +59,21 @@ public final class LastLocationRequest implements Parcelable {
}
/**
+ * Returns true if this request may access GNSS even if location settings would normally deny
+ * this, in order to enable automotive safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+ * Driving Assistance Systems) application.
+ *
+ * @return true if all limiting factors will be ignored to satisfy GNSS request
+ * @hide
+ */
+ // TODO: make this system api
+ public boolean isAdasGnssBypass() {
+ return mAdasGnssBypass;
+ }
+
+
+ /**
* Returns true if location settings, throttling, background location limits, and any other
* possible limiting factors will be ignored in order to satisfy this last location request.
*
@@ -65,12 +83,22 @@ public final class LastLocationRequest implements Parcelable {
return mLocationSettingsIgnored;
}
+ /**
+ * Returns true if any bypass flag is set on this request. For internal use only.
+ *
+ * @hide
+ */
+ public boolean isBypass() {
+ return mAdasGnssBypass || mLocationSettingsIgnored;
+ }
+
public static final @NonNull Parcelable.Creator<LastLocationRequest> CREATOR =
new Parcelable.Creator<LastLocationRequest>() {
@Override
public LastLocationRequest createFromParcel(Parcel in) {
return new LastLocationRequest(
/* hiddenFromAppOps= */ in.readBoolean(),
+ /* adasGnssBypass= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean());
}
@Override
@@ -86,6 +114,7 @@ public final class LastLocationRequest implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeBoolean(mHiddenFromAppOps);
+ parcel.writeBoolean(mAdasGnssBypass);
parcel.writeBoolean(mLocationSettingsIgnored);
}
@@ -99,12 +128,13 @@ public final class LastLocationRequest implements Parcelable {
}
LastLocationRequest that = (LastLocationRequest) o;
return mHiddenFromAppOps == that.mHiddenFromAppOps
+ && mAdasGnssBypass == that.mAdasGnssBypass
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored;
}
@Override
public int hashCode() {
- return Objects.hash(mHiddenFromAppOps, mLocationSettingsIgnored);
+ return Objects.hash(mHiddenFromAppOps, mAdasGnssBypass, mLocationSettingsIgnored);
}
@NonNull
@@ -115,8 +145,11 @@ public final class LastLocationRequest implements Parcelable {
if (mHiddenFromAppOps) {
s.append("hiddenFromAppOps, ");
}
+ if (mAdasGnssBypass) {
+ s.append("adasGnssBypass, ");
+ }
if (mLocationSettingsIgnored) {
- s.append("locationSettingsIgnored, ");
+ s.append("settingsBypass, ");
}
if (s.length() > "LastLocationRequest[".length()) {
s.setLength(s.length() - 2);
@@ -131,6 +164,7 @@ public final class LastLocationRequest implements Parcelable {
public static final class Builder {
private boolean mHiddenFromAppOps;
+ private boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
/**
@@ -138,6 +172,7 @@ public final class LastLocationRequest implements Parcelable {
*/
public Builder() {
mHiddenFromAppOps = false;
+ mAdasGnssBypass = false;
mLocationSettingsIgnored = false;
}
@@ -146,6 +181,7 @@ public final class LastLocationRequest implements Parcelable {
*/
public Builder(@NonNull LastLocationRequest lastLocationRequest) {
mHiddenFromAppOps = lastLocationRequest.mHiddenFromAppOps;
+ mAdasGnssBypass = lastLocationRequest.mAdasGnssBypass;
mLocationSettingsIgnored = lastLocationRequest.mLocationSettingsIgnored;
}
@@ -164,6 +200,25 @@ public final class LastLocationRequest implements Parcelable {
}
/**
+ * If set to true, indicates that the client is an ADAS (Advanced Driving Assistance
+ * Systems) client, which requires access to GNSS even if location settings would normally
+ * deny this, in order to enable auto safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS
+ * application. Defaults to false.
+ *
+ * <p>Permissions enforcement occurs when resulting location request is actually used, not
+ * when this method is invoked.
+ *
+ * @hide
+ */
+ // TODO: make this system api
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public @NonNull LastLocationRequest.Builder setAdasGnssBypass(boolean adasGnssBypass) {
+ mAdasGnssBypass = adasGnssBypass;
+ return this;
+ }
+
+ /**
* If set to true, indicates that location settings, throttling, background location limits,
* and any other possible limiting factors should be ignored in order to satisfy this
* last location request. This is only intended for use in user initiated emergency
@@ -186,6 +241,7 @@ public final class LastLocationRequest implements Parcelable {
public @NonNull LastLocationRequest build() {
return new LastLocationRequest(
mHiddenFromAppOps,
+ mAdasGnssBypass,
mLocationSettingsIgnored);
}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ae44c5e34521..526b84e85e38 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -315,6 +315,33 @@ public class LocationManager {
public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_ENABLED";
/**
+ * Broadcast intent action when the ADAS (Advanced Driving Assistance Systems) GNSS location
+ * enabled state changes. Includes a boolean intent extra, {@link #EXTRA_ADAS_GNSS_ENABLED},
+ * with the enabled state of ADAS GNSS location. This broadcast only has meaning on automotive
+ * devices.
+ *
+ * @see #EXTRA_ADAS_GNSS_ENABLED
+ * @see #isAdasGnssLocationEnabled()
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ADAS_GNSS_ENABLED_CHANGED =
+ "android.location.action.ADAS_GNSS_ENABLED_CHANGED";
+
+ /**
+ * Intent extra included with {@link #ACTION_ADAS_GNSS_ENABLED_CHANGED} broadcasts, containing
+ * the boolean enabled state of ADAS GNSS location.
+ *
+ * @see #ACTION_ADAS_GNSS_ENABLED_CHANGED
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ public static final String EXTRA_ADAS_GNSS_ENABLED = "android.location.extra.ADAS_GNSS_ENABLED";
+
+ /**
* Broadcast intent action indicating that a high power location requests
* has either started or stopped being active. The current state of
* active location requests should be read from AppOpsManager using
@@ -621,6 +648,42 @@ public class LocationManager {
}
/**
+ * Returns the current enabled/disabled state of ADAS (Advanced Driving Assistance Systems)
+ * GNSS location access for the given user. This controls safety critical automotive access to
+ * GNSS location. This only has meaning on automotive devices.
+ *
+ * @return true if ADAS location is enabled and false if ADAS location is disabled.
+ *
+ * @hide
+ */
+ //TODO: @SystemApi
+ public boolean isAdasGnssLocationEnabled() {
+ try {
+ return mService.isAdasGnssLocationEnabledForUser(mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables or disables ADAS (Advanced Driving Assistance Systems) GNSS location access for the
+ * given user. This only has meaning on automotive devices.
+ *
+ * @param enabled true to enable ADAS location and false to disable ADAS location.
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ @RequiresPermission(WRITE_SECURE_SETTINGS)
+ public void setAdasGnssLocationEnabled(boolean enabled) {
+ try {
+ mService.setAdasGnssLocationEnabledForUser(enabled, mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the current enabled/disabled status of the given provider. To listen for changes, see
* {@link #PROVIDERS_CHANGED_ACTION}.
*
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index a3842a1ffd0a..b48e59676ac1 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -194,6 +194,7 @@ public final class LocationRequest implements Parcelable {
private float mMinUpdateDistanceMeters;
private final long mMaxUpdateDelayMillis;
private boolean mHideFromAppOps;
+ private final boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
private boolean mLowPower;
private @Nullable WorkSource mWorkSource;
@@ -236,7 +237,7 @@ public final class LocationRequest implements Parcelable {
if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
quality = POWER_NONE;
} else if (LocationManager.GPS_PROVIDER.equals(provider)) {
- quality = ACCURACY_FINE;
+ quality = QUALITY_HIGH_ACCURACY;
} else {
quality = POWER_LOW;
}
@@ -289,6 +290,7 @@ public final class LocationRequest implements Parcelable {
float minUpdateDistanceMeters,
long maxUpdateDelayMillis,
boolean hiddenFromAppOps,
+ boolean adasGnssBypass,
boolean locationSettingsIgnored,
boolean lowPower,
WorkSource workSource) {
@@ -302,8 +304,9 @@ public final class LocationRequest implements Parcelable {
mMinUpdateDistanceMeters = minUpdateDistanceMeters;
mMaxUpdateDelayMillis = maxUpdateDelayMillis;
mHideFromAppOps = hiddenFromAppOps;
- mLowPower = lowPower;
+ mAdasGnssBypass = adasGnssBypass;
mLocationSettingsIgnored = locationSettingsIgnored;
+ mLowPower = lowPower;
mWorkSource = Objects.requireNonNull(workSource);
}
@@ -339,15 +342,15 @@ public final class LocationRequest implements Parcelable {
switch (quality) {
case POWER_HIGH:
// fall through
- case ACCURACY_FINE:
+ case QUALITY_HIGH_ACCURACY:
mQuality = QUALITY_HIGH_ACCURACY;
break;
- case ACCURACY_BLOCK:
+ case QUALITY_BALANCED_POWER_ACCURACY:
mQuality = QUALITY_BALANCED_POWER_ACCURACY;
break;
case POWER_LOW:
// fall through
- case ACCURACY_CITY:
+ case QUALITY_LOW_POWER:
mQuality = QUALITY_LOW_POWER;
break;
case POWER_NONE:
@@ -648,6 +651,21 @@ public final class LocationRequest implements Parcelable {
}
/**
+ * Returns true if this request may access GNSS even if location settings would normally deny
+ * this, in order to enable automotive safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+ * Driving Assistance Systems) application.
+ *
+ * @return true if all limiting factors will be ignored to satisfy GNSS request
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ public boolean isAdasGnssBypass() {
+ return mAdasGnssBypass;
+ }
+
+ /**
* @hide
* @deprecated LocationRequests should be treated as immutable.
*/
@@ -673,6 +691,15 @@ public final class LocationRequest implements Parcelable {
}
/**
+ * Returns true if any bypass flag is set on this request. For internal use only.
+ *
+ * @hide
+ */
+ public boolean isBypass() {
+ return mAdasGnssBypass || mLocationSettingsIgnored;
+ }
+
+ /**
* @hide
* @deprecated LocationRequests should be treated as immutable.
*/
@@ -749,6 +776,7 @@ public final class LocationRequest implements Parcelable {
/* minUpdateDistanceMeters= */ in.readFloat(),
/* maxUpdateDelayMillis= */ in.readLong(),
/* hiddenFromAppOps= */ in.readBoolean(),
+ /* adasGnssBypass= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean(),
/* lowPower= */ in.readBoolean(),
/* workSource= */ in.readTypedObject(WorkSource.CREATOR));
@@ -777,6 +805,7 @@ public final class LocationRequest implements Parcelable {
parcel.writeFloat(mMinUpdateDistanceMeters);
parcel.writeLong(mMaxUpdateDelayMillis);
parcel.writeBoolean(mHideFromAppOps);
+ parcel.writeBoolean(mAdasGnssBypass);
parcel.writeBoolean(mLocationSettingsIgnored);
parcel.writeBoolean(mLowPower);
parcel.writeTypedObject(mWorkSource, 0);
@@ -801,6 +830,7 @@ public final class LocationRequest implements Parcelable {
&& Float.compare(that.mMinUpdateDistanceMeters, mMinUpdateDistanceMeters) == 0
&& mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
&& mHideFromAppOps == that.mHideFromAppOps
+ && mAdasGnssBypass == that.mAdasGnssBypass
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored
&& mLowPower == that.mLowPower
&& Objects.equals(mProvider, that.mProvider)
@@ -866,8 +896,11 @@ public final class LocationRequest implements Parcelable {
if (mHideFromAppOps) {
s.append(", hiddenFromAppOps");
}
+ if (mAdasGnssBypass) {
+ s.append(", adasGnssBypass");
+ }
if (mLocationSettingsIgnored) {
- s.append(", locationSettingsIgnored");
+ s.append(", settingsBypass");
}
if (mWorkSource != null && !mWorkSource.isEmpty()) {
s.append(", ").append(mWorkSource);
@@ -889,6 +922,7 @@ public final class LocationRequest implements Parcelable {
private float mMinUpdateDistanceMeters;
private long mMaxUpdateDelayMillis;
private boolean mHiddenFromAppOps;
+ private boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
private boolean mLowPower;
@Nullable private WorkSource mWorkSource;
@@ -908,6 +942,7 @@ public final class LocationRequest implements Parcelable {
mMinUpdateDistanceMeters = 0;
mMaxUpdateDelayMillis = 0;
mHiddenFromAppOps = false;
+ mAdasGnssBypass = false;
mLocationSettingsIgnored = false;
mLowPower = false;
mWorkSource = null;
@@ -925,6 +960,7 @@ public final class LocationRequest implements Parcelable {
mMinUpdateDistanceMeters = locationRequest.mMinUpdateDistanceMeters;
mMaxUpdateDelayMillis = locationRequest.mMaxUpdateDelayMillis;
mHiddenFromAppOps = locationRequest.mHideFromAppOps;
+ mAdasGnssBypass = locationRequest.mAdasGnssBypass;
mLocationSettingsIgnored = locationRequest.mLocationSettingsIgnored;
mLowPower = locationRequest.mLowPower;
mWorkSource = locationRequest.mWorkSource;
@@ -977,10 +1013,10 @@ public final class LocationRequest implements Parcelable {
public @NonNull Builder setQuality(@NonNull Criteria criteria) {
switch (criteria.getAccuracy()) {
case Criteria.ACCURACY_COARSE:
- mQuality = ACCURACY_BLOCK;
+ mQuality = QUALITY_BALANCED_POWER_ACCURACY;
break;
case Criteria.ACCURACY_FINE:
- mQuality = ACCURACY_FINE;
+ mQuality = QUALITY_HIGH_ACCURACY;
break;
default: {
if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
@@ -1092,6 +1128,25 @@ public final class LocationRequest implements Parcelable {
}
/**
+ * If set to true, indicates that the client is an ADAS (Advanced Driving Assistance
+ * Systems) client, which requires access to GNSS even if location settings would normally
+ * deny this, in order to enable auto safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS
+ * application. Defaults to false.
+ *
+ * <p>Permissions enforcement occurs when resulting location request is actually used, not
+ * when this method is invoked.
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
+ mAdasGnssBypass = adasGnssBypass;
+ return this;
+ }
+
+ /**
* If set to true, indicates that location settings, throttling, background location limits,
* and any other possible limiting factors should be ignored in order to satisfy this
* request. This is only intended for use in user initiated emergency situations, and
@@ -1171,6 +1226,7 @@ public final class LocationRequest implements Parcelable {
mMinUpdateDistanceMeters,
mMaxUpdateDelayMillis,
mHiddenFromAppOps,
+ mAdasGnssBypass,
mLocationSettingsIgnored,
mLowPower,
new WorkSource(mWorkSource));
diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
index b72d36519e72..4f33a529a812 100644
--- a/location/java/android/location/provider/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -44,12 +44,19 @@ public final class ProviderRequest implements Parcelable {
public static final long INTERVAL_DISABLED = Long.MAX_VALUE;
public static final @NonNull ProviderRequest EMPTY_REQUEST = new ProviderRequest(
- INTERVAL_DISABLED, QUALITY_BALANCED_POWER_ACCURACY, 0, false, false, new WorkSource());
+ INTERVAL_DISABLED,
+ QUALITY_BALANCED_POWER_ACCURACY,
+ 0,
+ false,
+ false,
+ false,
+ new WorkSource());
private final long mIntervalMillis;
private final @Quality int mQuality;
private final long mMaxUpdateDelayMillis;
private final boolean mLowPower;
+ private final boolean mAdasGnssBypass;
private final boolean mLocationSettingsIgnored;
private final WorkSource mWorkSource;
@@ -72,12 +79,14 @@ public final class ProviderRequest implements Parcelable {
@Quality int quality,
long maxUpdateDelayMillis,
boolean lowPower,
+ boolean adasGnssBypass,
boolean locationSettingsIgnored,
@NonNull WorkSource workSource) {
mIntervalMillis = intervalMillis;
mQuality = quality;
mMaxUpdateDelayMillis = maxUpdateDelayMillis;
mLowPower = lowPower;
+ mAdasGnssBypass = adasGnssBypass;
mLocationSettingsIgnored = locationSettingsIgnored;
mWorkSource = Objects.requireNonNull(workSource);
}
@@ -126,6 +135,18 @@ public final class ProviderRequest implements Parcelable {
}
/**
+ * Returns true if this request may access GNSS even if location settings would normally deny
+ * this, in order to enable automotive safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+ * Driving Assistance Systems) application.
+ *
+ * @hide
+ */
+ public boolean isAdasGnssBypass() {
+ return mAdasGnssBypass;
+ }
+
+ /**
* Whether the provider should ignore all location settings, user consents, power restrictions
* or any other restricting factors and always satisfy this request to the best of their
* ability. This should only be used in case of a user initiated emergency.
@@ -135,6 +156,15 @@ public final class ProviderRequest implements Parcelable {
}
/**
+ * Returns true if any bypass flag is set on this request.
+ *
+ * @hide
+ */
+ public boolean isBypass() {
+ return mAdasGnssBypass || mLocationSettingsIgnored;
+ }
+
+ /**
* The power blame for this provider request.
*/
public @NonNull WorkSource getWorkSource() {
@@ -153,6 +183,7 @@ public final class ProviderRequest implements Parcelable {
/* quality= */ in.readInt(),
/* maxUpdateDelayMillis= */ in.readLong(),
/* lowPower= */ in.readBoolean(),
+ /* adasGnssBypass= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean(),
/* workSource= */ in.readTypedObject(WorkSource.CREATOR));
}
@@ -176,6 +207,7 @@ public final class ProviderRequest implements Parcelable {
parcel.writeInt(mQuality);
parcel.writeLong(mMaxUpdateDelayMillis);
parcel.writeBoolean(mLowPower);
+ parcel.writeBoolean(mAdasGnssBypass);
parcel.writeBoolean(mLocationSettingsIgnored);
parcel.writeTypedObject(mWorkSource, flags);
}
@@ -198,6 +230,7 @@ public final class ProviderRequest implements Parcelable {
&& mQuality == that.mQuality
&& mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
&& mLowPower == that.mLowPower
+ && mAdasGnssBypass == that.mAdasGnssBypass
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored
&& mWorkSource.equals(that.mWorkSource);
}
@@ -229,8 +262,11 @@ public final class ProviderRequest implements Parcelable {
if (mLowPower) {
s.append(", lowPower");
}
+ if (mAdasGnssBypass) {
+ s.append(", adasGnssBypass");
+ }
if (mLocationSettingsIgnored) {
- s.append(", locationSettingsIgnored");
+ s.append(", settingsBypass");
}
if (!mWorkSource.isEmpty()) {
s.append(", ").append(mWorkSource);
@@ -246,10 +282,12 @@ public final class ProviderRequest implements Parcelable {
* A Builder for {@link ProviderRequest}s.
*/
public static final class Builder {
+
private long mIntervalMillis = INTERVAL_DISABLED;
private int mQuality = QUALITY_BALANCED_POWER_ACCURACY;
private long mMaxUpdateDelayMillis = 0;
private boolean mLowPower;
+ private boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
private WorkSource mWorkSource = new WorkSource();
@@ -299,6 +337,16 @@ public final class ProviderRequest implements Parcelable {
}
/**
+ * Sets whether this ADAS request should bypass GNSS settings. False by default.
+ *
+ * @hide
+ */
+ public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
+ this.mAdasGnssBypass = adasGnssBypass;
+ return this;
+ }
+
+ /**
* Sets whether location settings should be ignored. False by default.
*/
public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
@@ -326,6 +374,7 @@ public final class ProviderRequest implements Parcelable {
mQuality,
mMaxUpdateDelayMillis,
mLowPower,
+ mAdasGnssBypass,
mLocationSettingsIgnored,
mWorkSource);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 5bb55222e09b..e891e5b64b3d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -16,6 +16,7 @@
package com.android.systemui.accessibility.floatingmenu;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.util.MathUtils.constrain;
import static android.util.MathUtils.sq;
import static android.view.WindowInsets.Type.ime;
@@ -200,6 +201,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
mListView = listView;
mWindowManager = context.getSystemService(WindowManager.class);
+ mLastConfiguration = new Configuration(getResources().getConfiguration());
mAdapter = new AccessibilityTargetAdapter(mTargets);
mUiHandler = createUiHandler();
mPosition = position;
@@ -243,7 +245,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout
}
});
- mLastConfiguration = new Configuration(getResources().getConfiguration());
initListView();
updateStrokeWith(getResources().getConfiguration().uiMode, mAlignment);
@@ -567,8 +568,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout
final int currentX = (int) event.getX();
final int currentY = (int) event.getY();
+ final int marginStartEnd = getMarginStartEndWith(mLastConfiguration);
final Rect touchDelegateBounds =
- new Rect(mMargin, mMargin, mMargin + getLayoutWidth(), mMargin + getLayoutHeight());
+ new Rect(marginStartEnd, mMargin, marginStartEnd + getLayoutWidth(),
+ mMargin + getLayoutHeight());
if (action == MotionEvent.ACTION_DOWN
&& touchDelegateBounds.contains(currentX, currentY)) {
mIsDownInEnlargedTouchArea = true;
@@ -682,15 +685,13 @@ public class AccessibilityFloatingMenuView extends FrameLayout
mListView.setLayoutManager(layoutManager);
mListView.addOnItemTouchListener(this);
mListView.animate().setInterpolator(new OvershootInterpolator());
- updateListView();
+ updateListViewWith(mLastConfiguration);
addView(mListView);
}
- private void updateListView() {
- final LayoutParams layoutParams = (FrameLayout.LayoutParams) mListView.getLayoutParams();
- layoutParams.setMargins(mMargin, mMargin, mMargin, mMargin);
- mListView.setLayoutParams(layoutParams);
+ private void updateListViewWith(Configuration configuration) {
+ updateMarginWith(configuration);
final int elevation =
getResources().getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
@@ -719,13 +720,15 @@ public class AccessibilityFloatingMenuView extends FrameLayout
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ mLastConfiguration.setTo(newConfig);
+
final int diff = newConfig.diff(mLastConfiguration);
if ((diff & ActivityInfo.CONFIG_LOCALE) != 0) {
updateAccessibilityTitle(mCurrentLayoutParams);
}
updateDimensions();
- updateListView();
+ updateListViewWith(newConfig);
updateItemViewWith(mSizeType);
updateColor();
updateStrokeWith(newConfig.uiMode, mAlignment);
@@ -733,8 +736,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout
updateRadiusWith(mSizeType, mRadiusType, mTargets.size());
updateScrollModeWith(hasExceededMaxLayoutHeight());
setSystemGestureExclusion();
-
- mLastConfiguration.setTo(newConfig);
}
@VisibleForTesting
@@ -756,11 +757,11 @@ public class AccessibilityFloatingMenuView extends FrameLayout
}
private int getMinWindowX() {
- return -mMargin;
+ return -getMarginStartEndWith(mLastConfiguration);
}
private int getMaxWindowX() {
- return mScreenWidth - mMargin - getLayoutWidth();
+ return mScreenWidth - getMarginStartEndWith(mLastConfiguration) - getLayoutWidth();
}
private int getMaxWindowY() {
@@ -805,6 +806,15 @@ public class AccessibilityFloatingMenuView extends FrameLayout
return layoutBottomY > imeY ? (layoutBottomY - imeY) : 0;
}
+ private void updateMarginWith(Configuration configuration) {
+ // Avoid overlapping with system bars under landscape mode, update the margins of the menu
+ // to align the edge of system bars.
+ final int marginStartEnd = getMarginStartEndWith(configuration);
+ final LayoutParams layoutParams = (FrameLayout.LayoutParams) mListView.getLayoutParams();
+ layoutParams.setMargins(marginStartEnd, mMargin, marginStartEnd, mMargin);
+ mListView.setLayoutParams(layoutParams);
+ }
+
private void updateOffsetWith(@ShapeType int shapeType, @Alignment int side) {
final float halfWidth = getLayoutWidth() / 2.0f;
final float offset = (shapeType == ShapeType.OVAL) ? 0 : halfWidth;
@@ -896,6 +906,12 @@ public class AccessibilityFloatingMenuView extends FrameLayout
return (mPadding + mIconHeight) * mTargets.size() + mPadding;
}
+ private int getMarginStartEndWith(Configuration configuration) {
+ return configuration != null
+ && configuration.orientation == ORIENTATION_PORTRAIT
+ ? mMargin : 0;
+ }
+
private @DimenRes int getRadiusResId(@SizeType int sizeType, int itemCount) {
return sizeType == SizeType.SMALL
? getSmallSizeResIdWith(itemCount)
@@ -932,7 +948,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
}
private int getWindowWidth() {
- return mMargin * 2 + getLayoutWidth();
+ return getMarginStartEndWith(mLastConfiguration) * 2 + getLayoutWidth();
}
private int getWindowHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 746621dfda27..d85c9a718871 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -231,7 +231,8 @@ public class DependencyProvider {
@Main Handler mainHandler,
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ UserTracker userTracker) {
return new NavigationBarController(context,
windowManager,
assistManagerLazy,
@@ -256,7 +257,8 @@ public class DependencyProvider {
mainHandler,
uiEventLogger,
navBarOverlayController,
- configurationController);
+ configurationController,
+ userTracker);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index da09793580bb..c6d7e7c46abb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -129,6 +129,7 @@ import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.AutoHideUiElement;
@@ -199,6 +200,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
+ private final UserTracker mUserTracker;
private Bundle mSavedState;
private NavigationBarView mNavigationBarView;
@@ -459,7 +461,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
SystemActions systemActions,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
mAccessibilityManager = accessibilityManager;
@@ -484,6 +487,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
+ mUserTracker = userTracker;
mNavBarMode = mNavigationModeController.addListener(this);
mAccessibilityButtonModeObserver.addListener(this);
@@ -1450,12 +1454,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
boolean longPressDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
- mLongPressHomeEnabled = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0) != 0;
+ mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
boolean gestureDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
- mAssistantTouchGestureEnabled = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0) != 0;
+ mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
if (mOverviewProxyService.getProxy() != null) {
try {
mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 8b5a537ba242..53592101c3ea 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -57,6 +57,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -116,6 +117,7 @@ public class NavigationBarController implements Callbacks,
private final TaskbarDelegate mTaskbarDelegate;
private int mNavMode;
private boolean mIsTablet;
+ private final UserTracker mUserTracker;
/** A displayId - nav bar maps. */
@VisibleForTesting
@@ -151,7 +153,8 @@ public class NavigationBarController implements Callbacks,
@Main Handler mainHandler,
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
mAssistManagerLazy = assistManagerLazy;
@@ -184,6 +187,7 @@ public class NavigationBarController implements Callbacks,
mNavigationModeController.addListener(this);
mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService);
mIsTablet = isTablet(mContext.getResources().getConfiguration());
+ mUserTracker = userTracker;
}
@Override
@@ -361,7 +365,8 @@ public class NavigationBarController implements Callbacks,
mSystemActions,
mHandler,
mNavBarOverlayController,
- mUiEventLogger);
+ mUiEventLogger,
+ mUserTracker);
mNavigationBars.put(displayId, navBar);
View navigationBarView = navBar.createView(savedState);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 669046591170..3c830cc374ab 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -453,14 +453,6 @@ public class ScreenshotView extends FrameLayout implements
mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
final float currentScale = 1 / cornerScale;
- mScreenshotPreview.setScaleX(currentScale);
- mScreenshotPreview.setScaleY(currentScale);
-
- if (mAccessibilityManager.isEnabled()) {
- mDismissButton.setAlpha(0);
- mDismissButton.setVisibility(View.VISIBLE);
- }
-
AnimatorSet dropInAnimation = new AnimatorSet();
ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
@@ -491,6 +483,20 @@ public class ScreenshotView extends FrameLayout implements
ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
+
+ toCorner.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mScreenshotPreview.setScaleX(currentScale);
+ mScreenshotPreview.setScaleY(currentScale);
+ mScreenshotPreview.setVisibility(View.VISIBLE);
+ if (mAccessibilityManager.isEnabled()) {
+ mDismissButton.setAlpha(0);
+ mDismissButton.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+
float xPositionPct =
SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
float dismissPct =
@@ -534,13 +540,6 @@ public class ScreenshotView extends FrameLayout implements
}
});
- toCorner.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mScreenshotPreview.setVisibility(View.VISIBLE);
- }
- });
-
mScreenshotFlash.setAlpha(0f);
mScreenshotFlash.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 452d69369d71..f03a9a8f3589 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -96,6 +96,15 @@ class NotificationShadeDepthController @Inject constructor(
var globalActionsSpring = DepthAnimation()
var showingHomeControls: Boolean = false
+ @VisibleForTesting
+ var brightnessMirrorSpring = DepthAnimation()
+ var brightnessMirrorVisible: Boolean = false
+ set(value) {
+ field = value
+ brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f)
+ else 0)
+ }
+
var qsPanelExpansion = 0f
set(value) {
if (field == value) return
@@ -189,10 +198,13 @@ class NotificationShadeDepthController @Inject constructor(
if (scrimsVisible || !blurUtils.supportsBlursOnWindows()) {
blur = 0
}
+ val zoomOut = blurUtils.ratioOfBlurRadius(blur)
+
+ // Brightness slider removes blur, but doesn't affect zooms
+ blur = (blur * (1f - brightnessMirrorSpring.ratio)).toInt()
val opaque = scrimsVisible && !ignoreShadeBlurUntilHidden
blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur, opaque)
- val zoomOut = blurUtils.ratioOfBlurRadius(blur)
try {
if (root.isAttachedToWindow && root.windowToken != null) {
wallpaperManager.setWallpaperZoomOut(root.windowToken, zoomOut)
@@ -260,6 +272,7 @@ class NotificationShadeDepthController @Inject constructor(
shadeSpring.finishIfRunning()
shadeAnimation.finishIfRunning()
globalActionsSpring.finishIfRunning()
+ brightnessMirrorSpring.finishIfRunning()
}
}
@@ -425,6 +438,7 @@ class NotificationShadeDepthController @Inject constructor(
it.println("shadeRadius: ${shadeSpring.radius}")
it.println("shadeAnimation: ${shadeAnimation.radius}")
it.println("globalActionsRadius: ${globalActionsSpring.radius}")
+ it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}")
it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
it.println("ignoreShadeBlurUntilHidden: $ignoreShadeBlurUntilHidden")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 399c8500ab48..a0edc7c494bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -74,11 +74,13 @@ public class BrightnessMirrorController
mBrightnessMirror.setVisibility(View.VISIBLE);
mVisibilityCallback.accept(true);
mNotificationPanel.setPanelAlpha(0, true /* animate */);
+ mDepthController.setBrightnessMirrorVisible(true);
}
public void hideMirror() {
mVisibilityCallback.accept(false);
mNotificationPanel.setPanelAlpha(255, true /* animate */);
+ mDepthController.setBrightnessMirrorVisible(false);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index ca1f55e95ff4..11ddbd045cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -241,7 +241,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
@Override
public void onReceive(Context context, Intent intent) {
boolean newWorkProfile = Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction());
- boolean userStarted = Intent.ACTION_USER_STARTED.equals(intent.getAction());
+ boolean userStarted = Intent.ACTION_USER_SWITCHED.equals(intent.getAction());
boolean isManagedProfile = mUserManager.isManagedProfile(
intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
if (userStarted || newWorkProfile) {
@@ -288,7 +288,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
public void start() {
if (DEBUG) Log.d(TAG, "Start");
final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_STARTED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index c5e35a497956..8b394bfe35b7 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -16,13 +16,18 @@
package com.android.systemui.toast;
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+
import android.animation.Animator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -53,7 +58,7 @@ public class SystemUIToast implements ToastPlugin.Toast {
final ToastPlugin.Toast mPluginToast;
private final String mPackageName;
- private final int mUserId;
+ @UserIdInt private final int mUserId;
private final LayoutInflater mLayoutInflater;
final int mDefaultX = 0;
@@ -74,7 +79,7 @@ public class SystemUIToast implements ToastPlugin.Toast {
}
SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
- ToastPlugin.Toast pluginToast, String packageName, int userId,
+ ToastPlugin.Toast pluginToast, String packageName, @UserIdInt int userId,
int orientation) {
mLayoutInflater = layoutInflater;
mContext = context;
@@ -248,6 +253,15 @@ public class SystemUIToast implements ToastPlugin.Toast {
return null;
}
+ final Context userContext;
+ try {
+ userContext = context.createPackageContextAsUser("android",
+ 0, new UserHandle(userId));
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Could not create user package context");
+ return null;
+ }
+
final ApplicationsState appState =
ApplicationsState.getInstance((Application) context.getApplicationContext());
if (!appState.isUserAdded(userId)) {
@@ -255,9 +269,11 @@ public class SystemUIToast implements ToastPlugin.Toast {
+ "packageName=" + packageName);
return null;
}
+
+ final PackageManager packageManager = userContext.getPackageManager();
final AppEntry appEntry = appState.getEntry(packageName, userId);
if (appEntry == null || appEntry.info == null
- || !ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(appEntry)) {
+ || !showApplicationIcon(appEntry.info, packageManager)) {
return null;
}
@@ -265,7 +281,20 @@ public class SystemUIToast implements ToastPlugin.Toast {
UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
IconFactory iconFactory = IconFactory.obtain(context);
Bitmap iconBmp = iconFactory.createBadgedIconBitmap(
- appInfo.loadUnbadgedIcon(context.getPackageManager()), user, true).icon;
+ appInfo.loadUnbadgedIcon(packageManager), user, true).icon;
return new BitmapDrawable(context.getResources(), iconBmp);
}
+
+ private static boolean showApplicationIcon(ApplicationInfo appInfo,
+ PackageManager packageManager) {
+ if (hasFlag(appInfo.flags, FLAG_UPDATED_SYSTEM_APP)) {
+ return packageManager.getLaunchIntentForPackage(appInfo.packageName)
+ != null;
+ }
+ return !hasFlag(appInfo.flags, FLAG_SYSTEM);
+ }
+
+ private static boolean hasFlag(int flags, int flag) {
+ return (flags & flag) != 0;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index da63b8a3ca4b..4980f7406cee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -52,6 +52,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -108,7 +109,8 @@ public class NavigationBarControllerTest extends SysuiTestCase {
Dependency.get(Dependency.MAIN_HANDLER),
mock(UiEventLogger.class),
mock(NavigationBarOverlayController.class),
- mock(ConfigurationController.class)));
+ mock(ConfigurationController.class),
+ mock(UserTracker.class)));
initializeNavigationBars();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 4ec45b444c46..b1afeecf39f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -76,6 +76,7 @@ import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -276,7 +277,8 @@ public class NavigationBarTest extends SysuiTestCase {
mock(SystemActions.class),
mHandler,
mock(NavigationBarOverlayController.class),
- mUiEventLogger));
+ mUiEventLogger,
+ mock(UserTracker.class)));
}
private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 0fb259575c87..60b38892e776 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -72,6 +72,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation
@Mock private lateinit var shadeAnimation: NotificationShadeDepthController.DepthAnimation
@Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation
+ @Mock private lateinit var brightnessSpring: NotificationShadeDepthController.DepthAnimation
@Mock private lateinit var listener: NotificationShadeDepthController.DepthListener
@Mock private lateinit var dozeParameters: DozeParameters
@Captor private lateinit var scrimVisibilityCaptor: ArgumentCaptor<Consumer<Int>>
@@ -91,6 +92,9 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
`when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
(answer.arguments[0] as Float * maxBlur).toInt()
}
+ `when`(blurUtils.ratioOfBlurRadius(anyInt())).then { answer ->
+ answer.arguments[0] as Int / maxBlur.toFloat()
+ }
`when`(blurUtils.supportsBlursOnWindows()).thenReturn(true)
`when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
`when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
@@ -101,6 +105,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
notificationShadeWindowController, dozeParameters, dumpManager)
notificationShadeDepthController.shadeSpring = shadeSpring
notificationShadeDepthController.shadeAnimation = shadeAnimation
+ notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
notificationShadeDepthController.globalActionsSpring = globalActionsSpring
notificationShadeDepthController.root = root
@@ -277,6 +282,32 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
}
@Test
+ fun brightnessMirrorVisible_whenVisible() {
+ notificationShadeDepthController.brightnessMirrorVisible = true
+ verify(brightnessSpring).animateTo(eq(maxBlur), any())
+ }
+
+ @Test
+ fun brightnessMirrorVisible_whenHidden() {
+ notificationShadeDepthController.brightnessMirrorVisible = false
+ verify(brightnessSpring).animateTo(eq(0), any())
+ }
+
+ @Test
+ fun brightnessMirror_hidesShadeBlur() {
+ // Brightness mirror is fully visible
+ `when`(brightnessSpring.ratio).thenReturn(1f)
+ // And shade is blurred
+ `when`(shadeSpring.radius).thenReturn(maxBlur)
+ `when`(shadeAnimation.radius).thenReturn(maxBlur)
+
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+ verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0))
+ verify(wallpaperManager).setWallpaperZoomOut(any(), eq(1f))
+ verify(blurUtils).applyBlur(eq(viewRootImpl), eq(0), eq(false))
+ }
+
+ @Test
fun ignoreShadeBlurUntilHidden_whennNull_ignoresIfShadeHasNoBlur() {
`when`(shadeSpring.radius).thenReturn(0)
`when`(shadeAnimation.radius).thenReturn(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 208790b24d8a..1a24c113a0df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -415,13 +415,6 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
}
@Test
- public void onUserSwitch_setsTheme() {
- mBroadcastReceiver.getValue().onReceive(null,
- new Intent(Intent.ACTION_USER_STARTED));
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
- }
-
- @Test
public void onProfileAdded_setsTheme() {
mBroadcastReceiver.getValue().onReceive(null,
new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 312cde6c5152..de5f47dd5dbb 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -662,6 +662,26 @@ public final class AutofillManagerService
return false;
}
+ /**
+ * Requests a count of saved passwords from the current service.
+ *
+ * @return {@code true} if the request succeeded
+ */
+ // Called by Shell command
+ boolean requestSavedPasswordCount(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
+ enforceCallingPermissionForManagement();
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.requestSavedPasswordCount(receiver);
+ return true;
+ } else if (sVerbose) {
+ Slog.v(TAG, "requestSavedPasswordCount(): no service for " + userId);
+ }
+ }
+ return false;
+ }
+
private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
com.android.server.autofill.Helper.sDebug = debug;
android.view.autofill.Helper.sDebug = debug;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a80efc461176..5f2d4e82883c 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -76,6 +76,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.IResultReceiver;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.AutofillManagerService.DisabledInfoCache;
@@ -1181,6 +1182,15 @@ final class AutofillManagerServiceImpl
}
@GuardedBy("mLock")
+ void requestSavedPasswordCount(IResultReceiver receiver) {
+ RemoteFillService remoteService =
+ new RemoteFillService(
+ getContext(), mInfo.getServiceInfo().getComponentName(), mUserId,
+ /* callbacks= */ null, mMaster.isInstantServiceAllowed());
+ remoteService.onSavedPasswordCountRequest(receiver);
+ }
+
+ @GuardedBy("mLock")
@Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceLocked() {
if (mRemoteAugmentedAutofillService == null) {
final String serviceName = mMaster.mAugmentedAutofillResolver.getServiceName(mUserId);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 68e6290c987a..1eaa59a0871a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.autofill;
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
+import static android.service.autofill.AutofillService.EXTRA_RESULT;
import static com.android.server.autofill.AutofillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
@@ -89,6 +90,9 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand {
pw.println(" get bind-instant-service-allowed");
pw.println(" Gets whether binding to services provided by instant apps is allowed");
pw.println("");
+ pw.println(" get saved-password-count");
+ pw.println(" Gets the number of saved passwords in the current service.");
+ pw.println("");
pw.println(" set log_level [off | debug | verbose]");
pw.println(" Sets the Autofill log level.");
pw.println("");
@@ -145,6 +149,8 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand {
return getBindInstantService(pw);
case "default-augmented-service-enabled":
return getDefaultAugmentedServiceEnabled(pw);
+ case "saved-password-count":
+ return getSavedPasswordCount(pw);
default:
pw.println("Invalid set: " + what);
return -1;
@@ -342,6 +348,25 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand {
return 0;
}
+ private int getSavedPasswordCount(PrintWriter pw) {
+ final int userId = getNextIntArgRequired();
+ CountDownLatch latch = new CountDownLatch(1);
+ IResultReceiver resultReceiver = new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ pw.println("resultCode=" + resultCode);
+ if (resultCode == 0 && resultData != null) {
+ pw.println("value=" + resultData.getInt(EXTRA_RESULT));
+ }
+ latch.countDown();
+ }
+ };
+ if (mService.requestSavedPasswordCount(userId, resultReceiver)) {
+ waitForLatch(pw, latch);
+ }
+ return 0;
+ }
+
private int requestDestroy(PrintWriter pw) {
if (!isNextArgSessions(pw)) {
return -1;
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index b0755ac836e0..94872b09cd36 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -41,6 +41,7 @@ import android.util.Slog;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.IResultReceiver;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
@@ -225,6 +226,10 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> {
}));
}
+ void onSavedPasswordCountRequest(IResultReceiver receiver) {
+ run(service -> service.onSavedPasswordCountRequest(receiver));
+ }
+
public void destroy() {
unbind();
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 697f6fa8eea8..ae1cd51e11f0 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -21,10 +21,18 @@ import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static com.android.server.wm.CompatModePackages.DOWNSCALED;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
import android.Manifest;
@@ -213,6 +221,39 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
+ // Turn the raw string to the corresponding CompatChange id.
+ static long getCompatChangeId(String raw) {
+ switch (raw) {
+ case "0.3":
+ return DOWNSCALE_30;
+ case "0.35":
+ return DOWNSCALE_35;
+ case "0.4":
+ return DOWNSCALE_40;
+ case "0.45":
+ return DOWNSCALE_45;
+ case "0.5":
+ return DOWNSCALE_50;
+ case "0.55":
+ return DOWNSCALE_55;
+ case "0.6":
+ return DOWNSCALE_60;
+ case "0.65":
+ return DOWNSCALE_65;
+ case "0.7":
+ return DOWNSCALE_70;
+ case "0.75":
+ return DOWNSCALE_75;
+ case "0.8":
+ return DOWNSCALE_80;
+ case "0.85":
+ return DOWNSCALE_85;
+ case "0.9":
+ return DOWNSCALE_90;
+ }
+ return 0;
+ }
+
/**
* GamePackageConfiguration manages all game mode config details for its associated package.
*/
@@ -331,19 +372,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
* Get the corresponding compat change id for the current scaling string.
*/
public long getCompatChangeId() {
- switch (mScaling) {
- case "0.5":
- return DOWNSCALE_50;
- case "0.6":
- return DOWNSCALE_60;
- case "0.7":
- return DOWNSCALE_70;
- case "0.8":
- return DOWNSCALE_80;
- case "0.9":
- return DOWNSCALE_90;
- }
- return 0;
+ return GameManagerService.getCompatChangeId(mScaling);
}
}
@@ -663,10 +692,18 @@ public final class GameManagerService extends IGameManagerService.Stub {
Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
overrides.put(DOWNSCALED, COMPAT_ENABLED);
+ overrides.put(DOWNSCALE_30, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_35, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_40, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_45, COMPAT_DISABLED);
overrides.put(DOWNSCALE_50, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_55, COMPAT_DISABLED);
overrides.put(DOWNSCALE_60, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_65, COMPAT_DISABLED);
overrides.put(DOWNSCALE_70, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_75, COMPAT_DISABLED);
overrides.put(DOWNSCALE_80, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_85, COMPAT_DISABLED);
overrides.put(DOWNSCALE_90, COMPAT_DISABLED);
overrides.put(scaleId, COMPAT_ENABLED);
final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 2074ffae1030..699f9e2d99f8 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -16,6 +16,21 @@
package com.android.server.app;
+import static com.android.server.wm.CompatModePackages.DOWNSCALED;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
+
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.IGameManagerService;
@@ -27,7 +42,6 @@ import android.util.ArraySet;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.server.compat.PlatformCompat;
-import com.android.server.wm.CompatModePackages;
import java.io.PrintWriter;
import java.util.Set;
@@ -43,12 +57,21 @@ public class GameManagerShellCommand extends ShellCommand {
public GameManagerShellCommand() {}
private static final ArraySet<Long> DOWNSCALE_CHANGE_IDS = new ArraySet<>(new Long[]{
- CompatModePackages.DOWNSCALED,
- CompatModePackages.DOWNSCALE_90,
- CompatModePackages.DOWNSCALE_80,
- CompatModePackages.DOWNSCALE_70,
- CompatModePackages.DOWNSCALE_60,
- CompatModePackages.DOWNSCALE_50});
+ DOWNSCALED,
+ DOWNSCALE_90,
+ DOWNSCALE_85,
+ DOWNSCALE_80,
+ DOWNSCALE_75,
+ DOWNSCALE_70,
+ DOWNSCALE_65,
+ DOWNSCALE_60,
+ DOWNSCALE_55,
+ DOWNSCALE_50,
+ DOWNSCALE_45,
+ DOWNSCALE_40,
+ DOWNSCALE_35,
+ DOWNSCALE_30,
+ });
@Override
public int onCommand(String cmd) {
@@ -62,32 +85,9 @@ public class GameManagerShellCommand extends ShellCommand {
final String ratio = getNextArgRequired();
final String packageName = getNextArgRequired();
- final long changeId;
- switch (ratio) {
- case "0.5":
- changeId = CompatModePackages.DOWNSCALE_50;
- break;
- case "0.6":
- changeId = CompatModePackages.DOWNSCALE_60;
- break;
- case "0.7":
- changeId = CompatModePackages.DOWNSCALE_70;
- break;
- case "0.8":
- changeId = CompatModePackages.DOWNSCALE_80;
- break;
- case "0.9":
- changeId = CompatModePackages.DOWNSCALE_90;
- break;
- case "disable":
- changeId = 0;
- break;
- default:
- changeId = -1;
- pw.println("Invalid scaling ratio '" + ratio + "'");
- break;
- }
- if (changeId == -1) {
+ final long changeId = GameManagerService.getCompatChangeId(ratio);
+ if (changeId == 0 && !ratio.equals("disable")) {
+ pw.println("Invalid scaling ratio '" + ratio + "'");
break;
}
@@ -96,10 +96,10 @@ public class GameManagerShellCommand extends ShellCommand {
if (changeId == 0) {
disabled = DOWNSCALE_CHANGE_IDS;
} else {
- enabled.add(CompatModePackages.DOWNSCALED);
+ enabled.add(DOWNSCALED);
enabled.add(changeId);
disabled = DOWNSCALE_CHANGE_IDS.stream()
- .filter(it -> it != CompatModePackages.DOWNSCALED && it != changeId)
+ .filter(it -> it != DOWNSCALED && it != changeId)
.collect(Collectors.toSet());
}
@@ -204,7 +204,7 @@ public class GameManagerShellCommand extends ShellCommand {
pw.println("Game manager (game) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" downscale [0.5|0.6|0.7|0.8|0.9|disable] <PACKAGE_NAME>");
+ pw.println(" downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65|0.7|0.75|0.8|0.85|0.9|disable] <PACKAGE_NAME>");
pw.println(" Force app to run at the specified scaling ratio.");
pw.println(" mode [--user <USER_ID>] [1|2|3|standard|performance|battery] <PACKAGE_NAME>");
pw.println(" Force app to run in the specified game mode, if supported.");
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 99a33e4462e2..5ba75d3ac312 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -19,11 +19,13 @@ package com.android.server.appop;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
import static android.app.AppOpsManager.FILTER_BY_UID;
+import static android.app.AppOpsManager.HISTORY_FLAG_GET_ATTRIBUTION_CHAINS;
import static android.app.AppOpsManager.HistoricalOpsRequestFilter;
import static android.app.AppOpsManager.KEY_BG_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.KEY_FG_SERVICE_STATE_SETTLE_TIME;
@@ -130,6 +132,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManagerInternal;
+import android.permission.PermissionManager;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -200,8 +203,8 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Scanner;
+import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
public class AppOpsService extends IAppOpsService.Stub {
@@ -2357,10 +2360,21 @@ public class AppOpsService extends IAppOpsService.Stub {
final String[] opNamesArray = (opNames != null)
? opNames.toArray(new String[opNames.size()]) : null;
+ Set<String> attributionChainExemptPackages = null;
+ if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
+ attributionChainExemptPackages =
+ PermissionManager.getIndicatorExemptedPackages(mContext);
+ }
+
+ final String[] chainExemptPkgArray = attributionChainExemptPackages != null
+ ? attributionChainExemptPackages.toArray(
+ new String[attributionChainExemptPackages.size()]) : null;
+
// Must not hold the appops lock
mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
- filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
+ filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
+ callback).recycleOnUse());
}
@Override
@@ -2377,10 +2391,21 @@ public class AppOpsService extends IAppOpsService.Stub {
final String[] opNamesArray = (opNames != null)
? opNames.toArray(new String[opNames.size()]) : null;
+ Set<String> attributionChainExemptPackages = null;
+ if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
+ attributionChainExemptPackages =
+ PermissionManager.getIndicatorExemptedPackages(mContext);
+ }
+
+ final String[] chainExemptPkgArray = attributionChainExemptPackages != null
+ ? attributionChainExemptPackages.toArray(
+ new String[attributionChainExemptPackages.size()]) : null;
+
// Must not hold the appops lock
mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
- filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
+ filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
+ callback).recycleOnUse());
}
@Override
@@ -3838,7 +3863,8 @@ public class AppOpsService extends IAppOpsService.Stub {
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
final boolean isProxyTrusted = mContext.checkPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
- == PackageManager.PERMISSION_GRANTED || isSelfBlame;
+ == PackageManager.PERMISSION_GRANTED || isSelfBlame
+ || attributionChainId != ATTRIBUTION_CHAIN_ID_NONE;
String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
proxiedPackageName);
@@ -5174,8 +5200,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
static class Shell extends ShellCommand {
- static final AtomicInteger sAttributionChainIds = new AtomicInteger(0);
-
final IAppOpsService mInterface;
final AppOpsService mInternal;
@@ -5645,8 +5669,7 @@ public class AppOpsService extends IAppOpsService.Stub {
shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
shell.packageName, shell.attributionTag, true, true,
"appops start shell command", true,
- AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR,
- shell.sAttributionChainIds.incrementAndGet());
+ AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, ATTRIBUTION_CHAIN_ID_NONE);
} else {
return -1;
}
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 1e32129dcee1..0439660f0c97 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -17,6 +17,8 @@
package com.android.server.appop;
import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
@@ -72,6 +74,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
/**
* This class manages information about recent accesses to ops for permission usage timeline.
@@ -270,13 +274,19 @@ final class DiscreteRegistry {
long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ Set<String> attributionExemptPkgs) {
+ boolean assembleChains = attributionExemptPkgs != null;
DiscreteOps discreteOps = getAllDiscreteOps();
+ ArrayMap<Integer, AttributionChain> attributionChains = new ArrayMap<>();
+ if (assembleChains) {
+ attributionChains = createAttributionChains(discreteOps, attributionExemptPkgs);
+ }
beginTimeMillis = max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
ChronoUnit.MILLIS).toEpochMilli());
discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
- opNamesFilter, attributionTagFilter, flagsFilter);
- discreteOps.applyToHistoricalOps(result);
+ opNamesFilter, attributionTagFilter, flagsFilter, attributionChains);
+ discreteOps.applyToHistoricalOps(result, attributionChains);
return;
}
@@ -317,13 +327,56 @@ final class DiscreteRegistry {
} finally {
try {
stream.close();
- } catch (IOException e) { }
+ } catch (IOException e) {
+ }
}
} else {
return 0;
}
}
+ private ArrayMap<Integer, AttributionChain> createAttributionChains(
+ DiscreteOps discreteOps, Set<String> attributionExemptPkgs) {
+ ArrayMap<Integer, AttributionChain> chains = new ArrayMap<>();
+ int nUids = discreteOps.mUids.size();
+ for (int uidNum = 0; uidNum < nUids; uidNum++) {
+ ArrayMap<String, DiscretePackageOps> pkgs = discreteOps.mUids.valueAt(uidNum).mPackages;
+ int uid = discreteOps.mUids.keyAt(uidNum);
+ int nPackages = pkgs.size();
+ for (int pkgNum = 0; pkgNum < nPackages; pkgNum++) {
+ ArrayMap<Integer, DiscreteOp> ops = pkgs.valueAt(pkgNum).mPackageOps;
+ String pkg = pkgs.keyAt(pkgNum);
+ int nOps = ops.size();
+ for (int opNum = 0; opNum < nOps; opNum++) {
+ ArrayMap<String, List<DiscreteOpEvent>> attrOps =
+ ops.valueAt(opNum).mAttributedOps;
+ int op = ops.keyAt(opNum);
+ int nAttrOps = attrOps.size();
+ for (int attrOpNum = 0; attrOpNum < nAttrOps; attrOpNum++) {
+ List<DiscreteOpEvent> opEvents = attrOps.valueAt(attrOpNum);
+ String attributionTag = attrOps.keyAt(attrOpNum);
+ int nOpEvents = opEvents.size();
+ for (int opEventNum = 0; opEventNum < nOpEvents; opEventNum++) {
+ DiscreteOpEvent event = opEvents.get(opEventNum);
+ if (event == null
+ || event.mAttributionChainId == ATTRIBUTION_CHAIN_ID_NONE) {
+ continue;
+ }
+
+ if (!chains.containsKey(event.mAttributionChainId)) {
+ chains.put(event.mAttributionChainId,
+ new AttributionChain(attributionExemptPkgs));
+ }
+ chains.get(event.mAttributionChainId)
+ .addEvent(pkg, uid, attributionTag, op, event);
+ }
+ }
+ }
+ }
+ }
+ return chains;
+ }
+
private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
synchronized (mOnDiskLock) {
long beginTimeMillis = Instant.now().minus(sDiscreteHistoryCutoff,
@@ -389,7 +442,7 @@ final class DiscreteRegistry {
String[] opNamesFilter = dumpOp == OP_NONE ? null
: new String[]{AppOpsManager.opToPublicName(dumpOp)};
discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
- opNamesFilter, attributionTagFilter, OP_FLAGS_ALL);
+ opNamesFilter, attributionTagFilter, OP_FLAGS_ALL, new ArrayMap<>());
pw.print(prefix);
pw.print("Largest chain id: ");
pw.print(mDiscreteOps.mLargestChainId);
@@ -419,6 +472,134 @@ final class DiscreteRegistry {
}
}
+ /**
+ * Represents a chain of usages, each attributing its usage to the one before it
+ */
+ private static final class AttributionChain {
+ private static final class OpEvent {
+ String mPkgName;
+ int mUid;
+ String mAttributionTag;
+ int mOpCode;
+ DiscreteOpEvent mOpEvent;
+
+ OpEvent(String pkgName, int uid, String attributionTag, int opCode,
+ DiscreteOpEvent event) {
+ mPkgName = pkgName;
+ mUid = uid;
+ mAttributionTag = attributionTag;
+ mOpCode = opCode;
+ mOpEvent = event;
+ }
+
+ public boolean matches(String pkgName, int uid, String attributionTag, int opCode,
+ DiscreteOpEvent event) {
+ return Objects.equals(pkgName, mPkgName) && mUid == uid
+ && Objects.equals(attributionTag, mAttributionTag) && mOpCode == opCode
+ && mOpEvent.mAttributionChainId == event.mAttributionChainId
+ && mOpEvent.mAttributionFlags == event.mAttributionFlags
+ && mOpEvent.mNoteTime == event.mNoteTime;
+ }
+
+ public boolean packageOpEquals(OpEvent other) {
+ return Objects.equals(other.mPkgName, mPkgName) && other.mUid == mUid
+ && Objects.equals(other.mAttributionTag, mAttributionTag)
+ && mOpCode == other.mOpCode;
+ }
+
+ public boolean equalsExceptDuration(OpEvent other) {
+ if (other.mOpEvent.mNoteDuration == mOpEvent.mNoteDuration) {
+ return false;
+ }
+ return packageOpEquals(other) && mOpEvent.equalsExceptDuration(other.mOpEvent);
+ }
+ }
+
+ ArrayList<OpEvent> mChain = new ArrayList<>();
+ Set<String> mExemptPkgs;
+ OpEvent mStartEvent = null;
+ OpEvent mLastVisibleEvent = null;
+
+ AttributionChain(Set<String> exemptPkgs) {
+ mExemptPkgs = exemptPkgs;
+ }
+
+ boolean isComplete() {
+ return !mChain.isEmpty() && getStart() != null && isEnd(mChain.get(mChain.size() - 1));
+ }
+
+ boolean isStart(String pkgName, int uid, String attributionTag, int op,
+ DiscreteOpEvent opEvent) {
+ if (mStartEvent == null || opEvent == null) {
+ return false;
+ }
+ return mStartEvent.matches(pkgName, uid, attributionTag, op, opEvent);
+ }
+
+ private OpEvent getStart() {
+ return mChain.isEmpty() || !isStart(mChain.get(0)) ? null : mChain.get(0);
+ }
+
+ private OpEvent getLastVisible() {
+ // Search all nodes but the first one, which is the start node
+ for (int i = mChain.size() - 1; i > 0; i--) {
+ OpEvent event = mChain.get(i);
+ if (!mExemptPkgs.contains(event.mPkgName)) {
+ return event;
+ }
+ }
+ return null;
+ }
+
+ void addEvent(String pkgName, int uid, String attributionTag, int op,
+ DiscreteOpEvent opEvent) {
+ OpEvent event = new OpEvent(pkgName, uid, attributionTag, op, opEvent);
+
+ // check if we have a matching event, without duration, replacing duration otherwise
+ for (int i = 0; i < mChain.size(); i++) {
+ OpEvent item = mChain.get(i);
+ if (item.equalsExceptDuration(event)) {
+ if (event.mOpEvent.mNoteDuration != -1) {
+ item.mOpEvent = event.mOpEvent;
+ }
+ return;
+ }
+ }
+
+ if (mChain.isEmpty() || isEnd(event)) {
+ mChain.add(event);
+ } else if (isStart(event)) {
+ mChain.add(0, event);
+
+ } else {
+ for (int i = 0; i < mChain.size(); i++) {
+ OpEvent currEvent = mChain.get(i);
+ if ((!isStart(currEvent)
+ && currEvent.mOpEvent.mNoteTime > event.mOpEvent.mNoteTime)
+ || i == mChain.size() - 1 && isEnd(currEvent)) {
+ mChain.add(i, event);
+ break;
+ } else if (i == mChain.size() - 1) {
+ mChain.add(event);
+ break;
+ }
+ }
+ }
+ mStartEvent = isComplete() ? getStart() : null;
+ mLastVisibleEvent = isComplete() ? getLastVisible() : null;
+ }
+
+ private boolean isEnd(OpEvent event) {
+ return event != null
+ && (event.mOpEvent.mAttributionFlags & ATTRIBUTION_FLAG_ACCESSOR) != 0;
+ }
+
+ private boolean isStart(OpEvent event) {
+ return event != null
+ && (event.mOpEvent.mAttributionFlags & ATTRIBUTION_FLAG_RECEIVER) != 0;
+ }
+ }
+
private final class DiscreteOps {
ArrayMap<Integer, DiscreteUidOps> mUids;
int mChainIdOffset;
@@ -448,25 +629,27 @@ final class DiscreteRegistry {
@Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
@AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
@AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
+ int offsetChainId = attributionChainId;
if (attributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
- attributionChainId += mChainIdOffset;
- if (attributionChainId < 0) {
- attributionChainId -= mChainIdOffset;
- mChainIdOffset = 0;
- mLargestChainId = attributionChainId;
- }
- if (attributionChainId > mLargestChainId) {
- mLargestChainId = attributionChainId;
+ offsetChainId = attributionChainId + mChainIdOffset;
+ if (offsetChainId > mLargestChainId) {
+ mLargestChainId = offsetChainId;
+ } else if (offsetChainId < 0) {
+ // handle overflow
+ offsetChainId = 0;
+ mLargestChainId = 0;
+ mChainIdOffset = -1 * attributionChainId;
}
}
getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, attributionTag, flags,
- uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
+ uidState, accessTime, accessDuration, attributionFlags, offsetChainId);
}
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
if ((filter & FILTER_BY_UID) != 0) {
ArrayMap<Integer, DiscreteUidOps> uids = new ArrayMap<>();
uids.put(uidFilter, getOrCreateDiscreteUidOps(uidFilter));
@@ -475,7 +658,8 @@ final class DiscreteRegistry {
int nUids = mUids.size();
for (int i = nUids - 1; i >= 0; i--) {
mUids.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, packageNameFilter,
- opNamesFilter, attributionTagFilter, flagsFilter);
+ opNamesFilter, attributionTagFilter, flagsFilter, mUids.keyAt(i),
+ attributionChains);
if (mUids.valueAt(i).isEmpty()) {
mUids.removeAt(i);
}
@@ -498,10 +682,11 @@ final class DiscreteRegistry {
}
}
- private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
+ private void applyToHistoricalOps(AppOpsManager.HistoricalOps result,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
int nUids = mUids.size();
for (int i = 0; i < nUids; i++) {
- mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i));
+ mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i), attributionChains);
}
}
@@ -668,7 +853,8 @@ final class DiscreteRegistry {
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ int currentUid, ArrayMap<Integer, AttributionChain> attributionChains) {
if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
ArrayMap<String, DiscretePackageOps> packages = new ArrayMap<>();
packages.put(packageNameFilter, getOrCreateDiscretePackageOps(packageNameFilter));
@@ -677,7 +863,8 @@ final class DiscreteRegistry {
int nPackages = mPackages.size();
for (int i = nPackages - 1; i >= 0; i--) {
mPackages.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, opNamesFilter,
- attributionTagFilter, flagsFilter);
+ attributionTagFilter, flagsFilter, currentUid, mPackages.keyAt(i),
+ attributionChains);
if (mPackages.valueAt(i).isEmpty()) {
mPackages.removeAt(i);
}
@@ -712,10 +899,12 @@ final class DiscreteRegistry {
return result;
}
- private void applyToHistory(AppOpsManager.HistoricalOps result, int uid) {
+ private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
+ @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
int nPackages = mPackages.size();
for (int i = 0; i < nPackages; i++) {
- mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i));
+ mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i),
+ attributionChains);
}
}
@@ -783,7 +972,8 @@ final class DiscreteRegistry {
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter,
@Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
- @AppOpsManager.OpFlags int flagsFilter) {
+ @AppOpsManager.OpFlags int flagsFilter, int currentUid, String currentPkgName,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
int nOps = mPackageOps.size();
for (int i = nOps - 1; i >= 0; i--) {
int opId = mPackageOps.keyAt(i);
@@ -793,7 +983,8 @@ final class DiscreteRegistry {
continue;
}
mPackageOps.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter,
- attributionTagFilter, flagsFilter);
+ attributionTagFilter, flagsFilter, currentUid, currentPkgName,
+ mPackageOps.keyAt(i), attributionChains);
if (mPackageOps.valueAt(i).isEmpty()) {
mPackageOps.removeAt(i);
}
@@ -817,11 +1008,12 @@ final class DiscreteRegistry {
}
private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
- @NonNull String packageName) {
+ @NonNull String packageName,
+ @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
int nPackageOps = mPackageOps.size();
for (int i = 0; i < nPackageOps; i++) {
mPackageOps.valueAt(i).applyToHistory(result, uid, packageName,
- mPackageOps.keyAt(i));
+ mPackageOps.keyAt(i), attributionChains);
}
}
@@ -880,7 +1072,9 @@ final class DiscreteRegistry {
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ int currentUid, String currentPkgName, int currentOp,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0) {
ArrayMap<String, List<DiscreteOpEvent>> attributedOps = new ArrayMap<>();
attributedOps.put(attributionTagFilter,
@@ -892,7 +1086,9 @@ final class DiscreteRegistry {
for (int i = nTags - 1; i >= 0; i--) {
String tag = mAttributedOps.keyAt(i);
List<DiscreteOpEvent> list = mAttributedOps.valueAt(i);
- list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter);
+ list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter,
+ currentUid, currentPkgName, currentOp, mAttributedOps.keyAt(i),
+ attributionChains);
mAttributedOps.put(tag, list);
if (list.size() == 0) {
mAttributedOps.removeAt(i);
@@ -954,7 +1150,8 @@ final class DiscreteRegistry {
}
private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
- @NonNull String packageName, int op) {
+ @NonNull String packageName, int op,
+ @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
int nOps = mAttributedOps.size();
for (int i = 0; i < nOps; i++) {
String tag = mAttributedOps.keyAt(i);
@@ -962,9 +1159,21 @@ final class DiscreteRegistry {
int nEvents = events.size();
for (int j = 0; j < nEvents; j++) {
DiscreteOpEvent event = events.get(j);
+ AppOpsManager.OpEventProxyInfo proxy = null;
+ if (event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE
+ && attributionChains != null) {
+ AttributionChain chain = attributionChains.get(event.mAttributionChainId);
+ if (chain != null && chain.isComplete()
+ && chain.isStart(packageName, uid, tag, op, event)
+ && chain.mLastVisibleEvent != null) {
+ AttributionChain.OpEvent proxyEvent = chain.mLastVisibleEvent;
+ proxy = new AppOpsManager.OpEventProxyInfo(proxyEvent.mUid,
+ proxyEvent.mPkgName, proxyEvent.mAttributionTag);
+ }
+ }
result.addDiscreteAccess(op, uid, packageName, tag, event.mUidState,
event.mOpFlag, discretizeTimeStamp(event.mNoteTime),
- discretizeDuration(event.mNoteDuration));
+ discretizeDuration(event.mNoteDuration), proxy);
}
}
}
@@ -1059,6 +1268,13 @@ final class DiscreteRegistry {
mAttributionChainId = attributionChainId;
}
+ public boolean equalsExceptDuration(DiscreteOpEvent o) {
+ return mNoteTime == o.mNoteTime && mUidState == o.mUidState && mOpFlag == o.mOpFlag
+ && mAttributionFlags == o.mAttributionFlags
+ && mAttributionChainId == o.mAttributionChainId;
+
+ }
+
private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
@NonNull Date date, @NonNull String prefix) {
pw.print(prefix);
@@ -1141,11 +1357,20 @@ final class DiscreteRegistry {
}
private static List<DiscreteOpEvent> filterEventsList(List<DiscreteOpEvent> list,
- long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter) {
+ long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter,
+ int currentUid, String currentPackageName, int currentOp, String currentAttrTag,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
int n = list.size();
List<DiscreteOpEvent> result = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
DiscreteOpEvent event = list.get(i);
+ AttributionChain chain = attributionChains.get(event.mAttributionChainId);
+ // If we have an attribution chain, and this event isn't the beginning node, remove it
+ if (chain != null && !chain.isStart(currentPackageName, currentUid, currentAttrTag,
+ currentOp, event) && chain.isComplete()
+ && event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+ continue;
+ }
if ((event.mOpFlag & flagsFilter) != 0
&& event.mNoteTime + event.mNoteDuration > beginTimeMillis
&& event.mNoteTime < endTimeMillis) {
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 62472b5f1d75..2c68aaf4f3e5 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -370,7 +370,7 @@ final class HistoricalRegistry {
@Nullable String attributionTag, @Nullable String[] opNames,
@OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter,
long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
- @NonNull RemoteCallback callback) {
+ String[] attributionExemptedPackages, @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
callback.sendResult(new Bundle());
return;
@@ -396,7 +396,7 @@ final class HistoricalRegistry {
if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
endTimeMillis, filter, uid, packageName, opNames, attributionTag,
- flags);
+ flags, new ArraySet<>(attributionExemptedPackages));
}
final Bundle payload = new Bundle();
@@ -407,7 +407,8 @@ final class HistoricalRegistry {
void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag,
@Nullable String[] opNames, @OpHistoryFlags int historyFlags,
@HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
- @OpFlags int flags, @NonNull RemoteCallback callback) {
+ @OpFlags int flags, @Nullable String[] attributionExemptPkgs,
+ @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
callback.sendResult(new Bundle());
return;
@@ -429,7 +430,8 @@ final class HistoricalRegistry {
if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
- endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags);
+ endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags,
+ new ArraySet<>(attributionExemptPkgs));
}
if ((historyFlags & HISTORY_FLAG_AGGREGATE) != 0) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index aeb1893c78b6..e6210b2a8cc9 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -45,6 +45,7 @@ import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
@@ -91,6 +92,7 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
@@ -134,6 +136,8 @@ import com.android.server.location.provider.PassiveLocationProvider;
import com.android.server.location.provider.PassiveLocationProviderManager;
import com.android.server.location.provider.StationaryThrottlingLocationProvider;
import com.android.server.location.provider.proxy.ProxyLocationProvider;
+import com.android.server.location.settings.LocationSettings;
+import com.android.server.location.settings.LocationUserSettings;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import java.io.FileDescriptor;
@@ -270,6 +274,8 @@ public class LocationManagerService extends ILocationManager.Stub implements
mGeofenceManager = new GeofenceManager(mContext, injector);
+ mInjector.getLocationSettings().registerLocationUserSettingsListener(
+ this::onLocationUserSettingsChanged);
mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
this::onLocationModeChanged);
mInjector.getSettingsHelper().addIgnoreSettingsAllowlistChangedListener(
@@ -476,6 +482,25 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
}
+ private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
+ boolean enabled = newSettings.isAdasGnssLocationEnabled();
+
+ if (D) {
+ Log.d(TAG, "[u" + userId + "] adas gnss location enabled = " + enabled);
+ }
+
+ EVENT_LOG.logAdasLocationEnabled(userId, enabled);
+
+ Intent intent = new Intent(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED)
+ .putExtra(LocationManager.EXTRA_ADAS_GNSS_ENABLED, enabled)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+ }
+ }
+
private void onLocationModeChanged(int userId) {
boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
LocationManager.invalidateLocalLocationEnabledCaches();
@@ -661,7 +686,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
// clients in the system process must have an attribution tag set
Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
- request = validateLocationRequest(request, identity);
+ request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
@@ -687,7 +712,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
new IllegalArgumentException());
}
- request = validateLocationRequest(request, identity);
+ request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
@@ -725,7 +750,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
}
- request = validateLocationRequest(request, identity);
+ request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
@@ -734,33 +759,27 @@ public class LocationManagerService extends ILocationManager.Stub implements
manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
}
- private LocationRequest validateLocationRequest(LocationRequest request,
+ private LocationRequest validateLocationRequest(String provider, LocationRequest request,
CallerIdentity identity) {
+ // validate unsanitized request
if (!request.getWorkSource().isEmpty()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_DEVICE_STATS,
"setting a work source requires " + permission.UPDATE_DEVICE_STATS);
}
- if (request.isHiddenFromAppOps()) {
- mContext.enforceCallingOrSelfPermission(
- permission.UPDATE_APP_OPS_STATS,
- "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
- }
- if (request.isLocationSettingsIgnored()) {
- mContext.enforceCallingOrSelfPermission(
- permission.WRITE_SECURE_SETTINGS,
- "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
- }
+ // sanitize request
LocationRequest.Builder sanitized = new LocationRequest.Builder(request);
- if (CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
- if (request.isLowPower()) {
- mContext.enforceCallingOrSelfPermission(
- permission.LOCATION_HARDWARE,
- "low power request requires " + permission.LOCATION_HARDWARE);
- }
- } else {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && GPS_PROVIDER.equals(provider)
+ && ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ sanitized.setAdasGnssBypass(true);
+ }
+
+ if (!CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
!= PERMISSION_GRANTED) {
sanitized.setLowPower(false);
@@ -786,7 +805,52 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
sanitized.setWorkSource(workSource);
- return sanitized.build();
+ request = sanitized.build();
+
+ // validate sanitized request
+ boolean isLocationProvider = mLocalService.isProvider(null, identity);
+
+ if (request.isLowPower() && CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS,
+ identity.getUid())) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.LOCATION_HARDWARE,
+ "low power request requires " + permission.LOCATION_HARDWARE);
+ }
+ if (request.isHiddenFromAppOps()) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.UPDATE_APP_OPS_STATS,
+ "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
+ }
+ if (request.isAdasGnssBypass()) {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on automotive devices");
+ }
+ if (!GPS_PROVIDER.equals(provider)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on the \"gps\" provider");
+ }
+ if (!ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ throw new SecurityException(
+ "only verified adas packages may use adas gnss bypass requests");
+ }
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
+ }
+ }
+ if (request.isLocationSettingsIgnored()) {
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ }
+ }
+
+ return request;
}
@Override
@@ -834,7 +898,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
// clients in the system process must have an attribution tag set
Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
- request = validateLastLocationRequest(request);
+ request = validateLastLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
@@ -844,16 +908,58 @@ public class LocationManagerService extends ILocationManager.Stub implements
return manager.getLastLocation(request, identity, permissionLevel);
}
- private LastLocationRequest validateLastLocationRequest(LastLocationRequest request) {
+ private LastLocationRequest validateLastLocationRequest(String provider,
+ LastLocationRequest request,
+ CallerIdentity identity) {
+ // sanitize request
+ LastLocationRequest.Builder sanitized = new LastLocationRequest.Builder(request);
+
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && GPS_PROVIDER.equals(provider)
+ && ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ sanitized.setAdasGnssBypass(true);
+ }
+
+ request = sanitized.build();
+
+ // validate request
+ boolean isLocationProvider = mLocalService.isProvider(null, identity);
+
if (request.isHiddenFromAppOps()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_APP_OPS_STATS,
"hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
}
+
+ if (request.isAdasGnssBypass()) {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on automotive devices");
+ }
+ if (!GPS_PROVIDER.equals(provider)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on the \"gps\" provider");
+ }
+ if (!ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ throw new SecurityException(
+ "only verified adas packages may use adas gnss bypass requests");
+ }
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
+ }
+ }
if (request.isLocationSettingsIgnored()) {
- mContext.enforceCallingOrSelfPermission(
- permission.WRITE_SECURE_SETTINGS,
- "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ }
}
return request;
@@ -1126,6 +1232,24 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
@Override
+ public void setAdasGnssLocationEnabledForUser(boolean enabled, int userId) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false, "setAdasGnssLocationEnabledForUser", null);
+
+ mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
+
+ mInjector.getLocationSettings().updateUserSettings(userId,
+ settings -> settings.withAdasGnssLocationEnabled(enabled));
+ }
+
+ @Override
+ public boolean isAdasGnssLocationEnabledForUser(int userId) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false, "isAdasGnssLocationEnabledForUser", null);
+ return mInjector.getLocationSettings().getUserSettings(userId).isAdasGnssLocationEnabled();
+ }
+
+ @Override
public boolean isProviderEnabledForUser(String provider, int userId) {
return mLocalService.isProviderEnabledForUser(provider, userId);
}
@@ -1555,11 +1679,12 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
}
- private static class SystemInjector implements Injector {
+ private static final class SystemInjector implements Injector {
private final Context mContext;
private final UserInfoHelper mUserInfoHelper;
+ private final LocationSettings mLocationSettings;
private final AlarmHelper mAlarmHelper;
private final SystemAppOpsHelper mAppOpsHelper;
private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
@@ -1584,6 +1709,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
mContext = context;
mUserInfoHelper = userInfoHelper;
+ mLocationSettings = new LocationSettings(context);
mAlarmHelper = new SystemAlarmHelper(context);
mAppOpsHelper = new SystemAppOpsHelper(context);
mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
@@ -1621,6 +1747,11 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
@Override
+ public LocationSettings getLocationSettings() {
+ return mLocationSettings;
+ }
+
+ @Override
public AlarmHelper getAlarmHelper() {
return mAlarmHelper;
}
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 937849309f96..b65338d9691d 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.location;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.provider.ProviderProperties;
@@ -60,6 +61,14 @@ class LocationShellCommand extends BasicShellCommandHandler {
handleSetLocationEnabled();
return 0;
}
+ case "is-adas-gnss-location-enabled": {
+ handleIsAdasGnssLocationEnabled();
+ return 0;
+ }
+ case "set-adas-gnss-location-enabled": {
+ handleSetAdasGnssLocationEnabled();
+ return 0;
+ }
case "providers": {
String command = getNextArgRequired();
return parseProvidersCommand(command);
@@ -134,6 +143,52 @@ class LocationShellCommand extends BasicShellCommandHandler {
mService.setLocationEnabledForUser(enabled, userId);
}
+ private void handleIsAdasGnssLocationEnabled() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalStateException("command only recognized on automotive devices");
+ }
+
+ int userId = UserHandle.USER_CURRENT_OR_SELF;
+
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ if ("--user".equals(option)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ } while (true);
+
+ getOutPrintWriter().println(mService.isAdasGnssLocationEnabledForUser(userId));
+ }
+
+ private void handleSetAdasGnssLocationEnabled() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalStateException("command only recognized on automotive devices");
+ }
+
+ boolean enabled = Boolean.parseBoolean(getNextArgRequired());
+
+ int userId = UserHandle.USER_CURRENT_OR_SELF;
+
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ if ("--user".equals(option)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ } while (true);
+
+ mService.setAdasGnssLocationEnabledForUser(enabled, userId);
+ }
+
private void handleAddTestProvider() {
String provider = getNextArgRequired();
@@ -297,6 +352,14 @@ class LocationShellCommand extends BasicShellCommandHandler {
pw.println(" set-location-enabled true|false [--user <USER_ID>]");
pw.println(" Sets the master location switch enabled state. If no user is specified,");
pw.println(" the current user is assumed.");
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ pw.println(" is-adas-gnss-location-enabled [--user <USER_ID>]");
+ pw.println(" Gets the ADAS GNSS location enabled state. If no user is specified,");
+ pw.println(" the current user is assumed.");
+ pw.println(" set-adas-gnss-location-enabled true|false [--user <USER_ID>]");
+ pw.println(" Sets the ADAS GNSS location enabled state. If no user is specified,");
+ pw.println(" the current user is assumed.");
+ }
pw.println(" providers");
pw.println(" The providers command is followed by a subcommand, as listed below:");
pw.println();
@@ -323,9 +386,8 @@ class LocationShellCommand extends BasicShellCommandHandler {
pw.println(" Common commands that may be supported by the gps provider, depending on");
pw.println(" hardware and software configurations:");
pw.println(" delete_aiding_data - requests deletion of any predictive aiding data");
- pw.println(" force_time_injection - requests NTP time injection to chipset");
- pw.println(" force_psds_injection - "
- + "requests predictive aiding data injection to chipset");
- pw.println(" request_power_stats - requests GNSS power stats update from chipset");
+ pw.println(" force_time_injection - requests NTP time injection");
+ pw.println(" force_psds_injection - requests predictive aiding data injection");
+ pw.println(" request_power_stats - requests GNSS power stats update");
}
}
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index e6d25ece93ef..db2a43f7a00d 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -55,19 +55,20 @@ public class LocationEventLog extends LocalEventLog {
private static final int EVENT_USER_SWITCHED = 1;
private static final int EVENT_LOCATION_ENABLED = 2;
- private static final int EVENT_PROVIDER_ENABLED = 3;
- private static final int EVENT_PROVIDER_MOCKED = 4;
- private static final int EVENT_PROVIDER_CLIENT_REGISTER = 5;
- private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 6;
- private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 7;
- private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 8;
- private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 9;
- private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 10;
- private static final int EVENT_PROVIDER_UPDATE_REQUEST = 11;
- private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 12;
- private static final int EVENT_PROVIDER_DELIVER_LOCATION = 13;
- private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 14;
- private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 15;
+ private static final int EVENT_ADAS_LOCATION_ENABLED = 3;
+ private static final int EVENT_PROVIDER_ENABLED = 4;
+ private static final int EVENT_PROVIDER_MOCKED = 5;
+ private static final int EVENT_PROVIDER_CLIENT_REGISTER = 6;
+ private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 7;
+ private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 8;
+ private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 9;
+ private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 10;
+ private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 11;
+ private static final int EVENT_PROVIDER_UPDATE_REQUEST = 12;
+ private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 13;
+ private static final int EVENT_PROVIDER_DELIVER_LOCATION = 14;
+ private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 15;
+ private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 16;
@GuardedBy("mAggregateStats")
private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
@@ -116,6 +117,11 @@ public class LocationEventLog extends LocalEventLog {
addLogEvent(EVENT_LOCATION_ENABLED, userId, enabled);
}
+ /** Logs a location enabled/disabled event. */
+ public void logAdasLocationEnabled(int userId, boolean enabled) {
+ addLogEvent(EVENT_ADAS_LOCATION_ENABLED, userId, enabled);
+ }
+
/** Logs a location provider enabled/disabled event. */
public void logProviderEnabled(String provider, int userId, boolean enabled) {
addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
@@ -219,6 +225,9 @@ public class LocationEventLog extends LocalEventLog {
return new UserSwitchedEvent(timeDelta, (Integer) args[0], (Integer) args[1]);
case EVENT_LOCATION_ENABLED:
return new LocationEnabledEvent(timeDelta, (Integer) args[0], (Boolean) args[1]);
+ case EVENT_ADAS_LOCATION_ENABLED:
+ return new LocationAdasEnabledEvent(timeDelta, (Integer) args[0],
+ (Boolean) args[1]);
case EVENT_PROVIDER_ENABLED:
return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
(Boolean) args[2]);
@@ -517,6 +526,23 @@ public class LocationEventLog extends LocalEventLog {
}
}
+ private static final class LocationAdasEnabledEvent extends LogEvent {
+
+ private final int mUserId;
+ private final boolean mEnabled;
+
+ LocationAdasEnabledEvent(long timeDelta, int userId, boolean enabled) {
+ super(timeDelta);
+ mUserId = userId;
+ mEnabled = enabled;
+ }
+
+ @Override
+ public String getLogString() {
+ return "adas location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled");
+ }
+ }
+
/**
* Aggregate statistics for a single package under a single provider.
*/
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 1cccf08b0367..f3dcfbbf2c0a 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -771,10 +771,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
boolean enabled = mContext.getSystemService(LocationManager.class)
.isLocationEnabledForUser(UserHandle.CURRENT);
- // .. but enable anyway, if there's an active settings-ignored request (e.g. ELS)
+ // .. but enable anyway, if there's an active bypass request (e.g. ELS or ADAS)
enabled |= (mProviderRequest != null
&& mProviderRequest.isActive()
- && mProviderRequest.isLocationSettingsIgnored());
+ && mProviderRequest.isBypass());
// ... and, finally, disable anyway, if device is being shut down
enabled &= !mShutdown;
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index b0351184ffe5..173fd13c11a1 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -17,6 +17,7 @@
package com.android.server.location.injector;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.settings.LocationSettings;
/**
* Injects various location dependencies so that they may be controlled by tests.
@@ -27,6 +28,9 @@ public interface Injector {
/** Returns a UserInfoHelper. */
UserInfoHelper getUserInfoHelper();
+ /** Returns a LocationSettings. */
+ LocationSettings getLocationSettings();
+
/** Returns an AlarmHelper. */
AlarmHelper getAlarmHelper();
diff --git a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
index c315da476d99..3e8da7d7478a 100644
--- a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
@@ -670,8 +670,6 @@ public class SystemSettingsHelper extends SettingsHelper {
}
}
-
-
private static class PackageTagsListSetting extends DeviceConfigSetting {
private final Supplier<ArrayMap<String, ArraySet<String>>> mBaseValuesSupplier;
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 8d335b83d99c..43886f7cb87b 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -113,6 +113,8 @@ import com.android.server.location.injector.UserInfoHelper;
import com.android.server.location.injector.UserInfoHelper.UserListener;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.RemoteListenerRegistration;
+import com.android.server.location.settings.LocationSettings;
+import com.android.server.location.settings.LocationUserSettings;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -549,6 +551,19 @@ public class LocationProviderManager extends
}
@GuardedBy("mLock")
+ final boolean onAdasGnssLocationEnabledChanged(int userId) {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mLock));
+ }
+
+ if (getIdentity().getUserId() == userId) {
+ return onProviderLocationRequestChanged();
+ }
+
+ return false;
+ }
+
+ @GuardedBy("mLock")
final boolean onForegroundChanged(int uid, boolean foreground) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
@@ -592,8 +607,8 @@ public class LocationProviderManager extends
onHighPowerUsageChanged();
updateService();
- // if location settings ignored has changed then the active state may have changed
- return oldRequest.isLocationSettingsIgnored() != newRequest.isLocationSettingsIgnored();
+ // if bypass state has changed then the active state may have changed
+ return oldRequest.isBypass() != newRequest.isBypass();
}
private LocationRequest calculateProviderLocationRequest() {
@@ -616,9 +631,24 @@ public class LocationProviderManager extends
if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains(
getIdentity().getPackageName(), getIdentity().getAttributionTag())
&& !mLocationManagerInternal.isProvider(null, getIdentity())) {
- builder.setLocationSettingsIgnored(false);
locationSettingsIgnored = false;
}
+
+ builder.setLocationSettingsIgnored(locationSettingsIgnored);
+ }
+
+ boolean adasGnssBypass = baseRequest.isAdasGnssBypass();
+ if (adasGnssBypass) {
+ // if we are not currently allowed use adas gnss bypass, disable it
+ if (!GPS_PROVIDER.equals(mName)) {
+ Log.e(TAG, "adas gnss bypass request received in non-gps provider");
+ adasGnssBypass = false;
+ } else if (!mLocationSettings.getUserSettings(
+ getIdentity().getUserId()).isAdasGnssLocationEnabled()) {
+ adasGnssBypass = false;
+ }
+
+ builder.setAdasGnssBypass(adasGnssBypass);
}
if (!locationSettingsIgnored && !isThrottlingExempt()) {
@@ -769,7 +799,7 @@ public class LocationProviderManager extends
Location lastLocation = getLastLocationUnsafe(
getIdentity().getUserId(),
getPermissionLevel(),
- getRequest().isLocationSettingsIgnored(),
+ getRequest().isBypass(),
maxLocationAgeMs);
if (lastLocation != null) {
executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation)));
@@ -1114,7 +1144,7 @@ public class LocationProviderManager extends
Location lastLocation = getLastLocationUnsafe(
getIdentity().getUserId(),
getPermissionLevel(),
- getRequest().isLocationSettingsIgnored(),
+ getRequest().isBypass(),
MAX_CURRENT_LOCATION_AGE_MS);
if (lastLocation != null) {
executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation)));
@@ -1267,6 +1297,7 @@ public class LocationProviderManager extends
private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
protected final LocationManagerInternal mLocationManagerInternal;
+ protected final LocationSettings mLocationSettings;
protected final SettingsHelper mSettingsHelper;
protected final UserInfoHelper mUserHelper;
protected final AlarmHelper mAlarmHelper;
@@ -1280,6 +1311,8 @@ public class LocationProviderManager extends
protected final LocationFudger mLocationFudger;
private final UserListener mUserChangedListener = this::onUserChanged;
+ private final LocationSettings.LocationUserSettingsListener mLocationUserSettingsListener =
+ this::onLocationUserSettingsChanged;
private final UserSettingChangedListener mLocationEnabledChangedListener =
this::onLocationEnabledChanged;
private final GlobalSettingChangedListener mBackgroundThrottlePackageWhitelistChangedListener =
@@ -1332,6 +1365,7 @@ public class LocationProviderManager extends
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
+ mLocationSettings = injector.getLocationSettings();
mSettingsHelper = injector.getSettingsHelper();
mUserHelper = injector.getUserInfoHelper();
mAlarmHelper = injector.getAlarmHelper();
@@ -1362,6 +1396,7 @@ public class LocationProviderManager extends
mStateChangedListener = listener;
mUserHelper.addListener(mUserChangedListener);
+ mLocationSettings.registerLocationUserSettingsListener(mLocationUserSettingsListener);
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
final long identity = Binder.clearCallingIdentity();
@@ -1389,6 +1424,7 @@ public class LocationProviderManager extends
}
mUserHelper.removeListener(mUserChangedListener);
+ mLocationSettings.unregisterLocationUserSettingsListener(mLocationUserSettingsListener);
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
// if external entities are registering listeners it's their responsibility to
@@ -1550,7 +1586,7 @@ public class LocationProviderManager extends
public @Nullable Location getLastLocation(LastLocationRequest request,
CallerIdentity identity, @PermissionLevel int permissionLevel) {
- if (!isActive(request.isLocationSettingsIgnored(), identity)) {
+ if (!isActive(request.isBypass(), identity)) {
return null;
}
@@ -1564,7 +1600,7 @@ public class LocationProviderManager extends
getLastLocationUnsafe(
identity.getUserId(),
permissionLevel,
- request.isLocationSettingsIgnored(),
+ request.isBypass(),
Long.MAX_VALUE),
permissionLevel);
@@ -1584,7 +1620,7 @@ public class LocationProviderManager extends
* location if necessary.
*/
public @Nullable Location getLastLocationUnsafe(int userId,
- @PermissionLevel int permissionLevel, boolean ignoreLocationSettings,
+ @PermissionLevel int permissionLevel, boolean isBypass,
long maximumAgeMs) {
if (userId == UserHandle.USER_ALL) {
// find the most recent location across all users
@@ -1592,7 +1628,7 @@ public class LocationProviderManager extends
final int[] runningUserIds = mUserHelper.getRunningUserIds();
for (int i = 0; i < runningUserIds.length; i++) {
Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel,
- ignoreLocationSettings, maximumAgeMs);
+ isBypass, maximumAgeMs);
if (lastLocation == null || (next != null && next.getElapsedRealtimeNanos()
> lastLocation.getElapsedRealtimeNanos())) {
lastLocation = next;
@@ -1601,7 +1637,7 @@ public class LocationProviderManager extends
return lastLocation;
} else if (userId == UserHandle.USER_CURRENT) {
return getLastLocationUnsafe(mUserHelper.getCurrentUserId(), permissionLevel,
- ignoreLocationSettings, maximumAgeMs);
+ isBypass, maximumAgeMs);
}
Preconditions.checkArgument(userId >= 0);
@@ -1613,7 +1649,7 @@ public class LocationProviderManager extends
if (lastLocation == null) {
location = null;
} else {
- location = lastLocation.get(permissionLevel, ignoreLocationSettings);
+ location = lastLocation.get(permissionLevel, isBypass);
}
}
@@ -1925,7 +1961,7 @@ public class LocationProviderManager extends
// provider, under the assumption that once we send the request off, the provider will
// immediately attempt to deliver a new location satisfying that request.
long delayMs;
- if (!oldRequest.isLocationSettingsIgnored() && newRequest.isLocationSettingsIgnored()) {
+ if (!oldRequest.isBypass() && newRequest.isBypass()) {
delayMs = 0;
} else if (newRequest.getIntervalMillis() > oldRequest.getIntervalMillis()) {
// if the interval has increased, tell the provider immediately, so it can save power
@@ -2002,12 +2038,12 @@ public class LocationProviderManager extends
return false;
}
- boolean locationSettingsIgnored = registration.getRequest().isLocationSettingsIgnored();
- if (!isActive(locationSettingsIgnored, registration.getIdentity())) {
+ boolean isBypass = registration.getRequest().isBypass();
+ if (!isActive(isBypass, registration.getIdentity())) {
return false;
}
- if (!locationSettingsIgnored) {
+ if (!isBypass) {
switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
case LOCATION_MODE_FOREGROUND_ONLY:
if (!registration.isForeground()) {
@@ -2036,15 +2072,15 @@ public class LocationProviderManager extends
return true;
}
- private boolean isActive(boolean locationSettingsIgnored, CallerIdentity identity) {
+ private boolean isActive(boolean isBypass, CallerIdentity identity) {
if (identity.isSystemServer()) {
- if (!locationSettingsIgnored) {
+ if (!isBypass) {
if (!isEnabled(mUserHelper.getCurrentUserId())) {
return false;
}
}
} else {
- if (!locationSettingsIgnored) {
+ if (!isBypass) {
if (!isEnabled(identity.getUserId())) {
return false;
}
@@ -2071,6 +2107,7 @@ public class LocationProviderManager extends
long intervalMs = ProviderRequest.INTERVAL_DISABLED;
int quality = LocationRequest.QUALITY_LOW_POWER;
long maxUpdateDelayMs = Long.MAX_VALUE;
+ boolean adasGnssBypass = false;
boolean locationSettingsIgnored = false;
boolean lowPower = true;
@@ -2086,6 +2123,7 @@ public class LocationProviderManager extends
intervalMs = min(request.getIntervalMillis(), intervalMs);
quality = min(request.getQuality(), quality);
maxUpdateDelayMs = min(request.getMaxUpdateDelayMillis(), maxUpdateDelayMs);
+ adasGnssBypass |= request.isAdasGnssBypass();
locationSettingsIgnored |= request.isLocationSettingsIgnored();
lowPower &= request.isLowPower();
}
@@ -2123,6 +2161,7 @@ public class LocationProviderManager extends
.setIntervalMillis(intervalMs)
.setQuality(quality)
.setMaxUpdateDelayMillis(maxUpdateDelayMs)
+ .setAdasGnssBypass(adasGnssBypass)
.setLocationSettingsIgnored(locationSettingsIgnored)
.setLowPower(lowPower)
.setWorkSource(workSource)
@@ -2191,6 +2230,16 @@ public class LocationProviderManager extends
}
}
+ private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
+ synchronized (mLock) {
+ updateRegistrations(
+ registration -> registration.onAdasGnssLocationEnabledChanged(userId));
+ }
+ }
+ }
+
private void onLocationEnabledChanged(int userId) {
synchronized (mLock) {
if (mState == STATE_STOPPED) {
@@ -2560,16 +2609,16 @@ public class LocationProviderManager extends
}
public @Nullable Location get(@PermissionLevel int permissionLevel,
- boolean ignoreLocationSettings) {
+ boolean isBypass) {
switch (permissionLevel) {
case PERMISSION_FINE:
- if (ignoreLocationSettings) {
+ if (isBypass) {
return mFineBypassLocation;
} else {
return mFineLocation;
}
case PERMISSION_COARSE:
- if (ignoreLocationSettings) {
+ if (isBypass) {
return mCoarseBypassLocation;
} else {
return mCoarseLocation;
diff --git a/services/core/java/com/android/server/location/settings/LocationSettings.java b/services/core/java/com/android/server/location/settings/LocationSettings.java
new file mode 100644
index 000000000000..d52153893970
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/LocationSettings.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 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.settings;
+
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+
+import android.content.Context;
+import android.os.Environment;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.FgThread;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Function;
+
+/**
+ * Accessor for location user settings. Ensure there is only ever one instance as multiple instances
+ * don't play nicely with each other.
+ */
+public class LocationSettings {
+
+ /** Listens for changes to location user settings. */
+ public interface LocationUserSettingsListener {
+ /** Invoked when location user settings have changed for the given user. */
+ void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings);
+ }
+
+ private static final String LOCATION_DIRNAME = "location";
+ private static final String LOCATION_SETTINGS_FILENAME = "settings";
+
+ final Context mContext;
+
+ @GuardedBy("mUserSettings")
+ private final SparseArray<LocationUserSettingsStore> mUserSettings;
+ private final CopyOnWriteArrayList<LocationUserSettingsListener> mUserSettingsListeners;
+
+ public LocationSettings(Context context) {
+ mContext = context;
+ mUserSettings = new SparseArray<>(1);
+ mUserSettingsListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /** Registers a listener for changes to location user settings. */
+ public final void registerLocationUserSettingsListener(LocationUserSettingsListener listener) {
+ mUserSettingsListeners.add(listener);
+ }
+
+ /** Unregisters a listener for changes to location user settings. */
+ public final void unregisterLocationUserSettingsListener(
+ LocationUserSettingsListener listener) {
+ mUserSettingsListeners.remove(listener);
+ }
+
+ protected File getUserSettingsDir(int userId) {
+ return Environment.getDataSystemDeDirectory(userId);
+ }
+
+ protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) {
+ return new LocationUserSettingsStore(userId, file);
+ }
+
+ private LocationUserSettingsStore getUserSettingsStore(int userId) {
+ synchronized (mUserSettings) {
+ LocationUserSettingsStore settingsStore = mUserSettings.get(userId);
+ if (settingsStore == null) {
+ File file = new File(new File(getUserSettingsDir(userId), LOCATION_DIRNAME),
+ LOCATION_SETTINGS_FILENAME);
+ settingsStore = createUserSettingsStore(userId, file);
+ mUserSettings.put(userId, settingsStore);
+ }
+ return settingsStore;
+ }
+ }
+
+ /** Retrieves the current state of location user settings. */
+ public final LocationUserSettings getUserSettings(int userId) {
+ return getUserSettingsStore(userId).get();
+ }
+
+ /** Updates the current state of location user settings for the given user. */
+ public final void updateUserSettings(int userId,
+ Function<LocationUserSettings, LocationUserSettings> updater) {
+ getUserSettingsStore(userId).update(updater);
+ }
+
+ @VisibleForTesting
+ final void flushFiles() throws InterruptedException {
+ synchronized (mUserSettings) {
+ int size = mUserSettings.size();
+ for (int i = 0; i < size; i++) {
+ mUserSettings.valueAt(i).flushFile();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ final void deleteFiles() throws InterruptedException {
+ synchronized (mUserSettings) {
+ int size = mUserSettings.size();
+ for (int i = 0; i < size; i++) {
+ mUserSettings.valueAt(i).deleteFile();
+ }
+ }
+ }
+
+ protected final void fireListeners(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ for (LocationUserSettingsListener listener : mUserSettingsListeners) {
+ listener.onLocationUserSettingsChanged(userId, oldSettings, newSettings);
+ }
+ }
+
+ class LocationUserSettingsStore extends SettingsStore<LocationUserSettings> {
+
+ protected final int mUserId;
+
+ LocationUserSettingsStore(int userId, File file) {
+ super(file);
+ mUserId = userId;
+ }
+
+ @Override
+ protected LocationUserSettings read(int version, DataInput in) throws IOException {
+ return filterSettings(LocationUserSettings.read(mContext.getResources(), version, in));
+ }
+
+ @Override
+ protected void write(DataOutput out, LocationUserSettings settings) throws IOException {
+ settings.write(out);
+ }
+
+ @Override
+ public void update(Function<LocationUserSettings, LocationUserSettings> updater) {
+ super.update(settings -> filterSettings(updater.apply(settings)));
+ }
+
+ @Override
+ protected void onChange(LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ FgThread.getExecutor().execute(() -> fireListeners(mUserId, oldSettings, newSettings));
+ }
+
+ private LocationUserSettings filterSettings(LocationUserSettings settings) {
+ if (settings.isAdasGnssLocationEnabled()
+ && !mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
+ // prevent non-automotive devices from ever enabling this
+ settings = settings.withAdasGnssLocationEnabled(false);
+ }
+ return settings;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/settings/LocationUserSettings.java b/services/core/java/com/android/server/location/settings/LocationUserSettings.java
new file mode 100644
index 000000000000..283255ef4b22
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/LocationUserSettings.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 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.settings;
+
+import android.content.res.Resources;
+
+import com.android.internal.R;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Objects;
+
+/** Holds the state of location user settings. */
+public final class LocationUserSettings implements SettingsStore.VersionedSettings {
+
+ // remember to bump this version code and add the appropriate upgrade logic whenever the format
+ // is changed.
+ private static final int VERSION = 1;
+
+ private final boolean mAdasGnssLocationEnabled;
+
+ private LocationUserSettings(boolean adasGnssLocationEnabled) {
+ mAdasGnssLocationEnabled = adasGnssLocationEnabled;
+ }
+
+ @Override
+ public int getVersion() {
+ return VERSION;
+ }
+
+ public boolean isAdasGnssLocationEnabled() {
+ return mAdasGnssLocationEnabled;
+ }
+
+ /** Returns an instance with ADAS GNSS location enabled state set as given. */
+ public LocationUserSettings withAdasGnssLocationEnabled(boolean adasEnabled) {
+ if (adasEnabled == mAdasGnssLocationEnabled) {
+ return this;
+ }
+
+ return new LocationUserSettings(adasEnabled);
+ }
+
+ void write(DataOutput out) throws IOException {
+ out.writeBoolean(mAdasGnssLocationEnabled);
+ }
+
+ static LocationUserSettings read(Resources resources, int version, DataInput in)
+ throws IOException {
+ boolean adasGnssLocationEnabled;
+
+ // upgrade code goes here. remember to bump the version field when changing the format
+ switch (version) {
+ default:
+ // set all fields to defaults
+ adasGnssLocationEnabled = resources.getBoolean(
+ R.bool.config_defaultAdasGnssLocationEnabled);
+ break;
+ case 1:
+ adasGnssLocationEnabled = in.readBoolean();
+ // fall through
+ }
+
+ return new LocationUserSettings(adasGnssLocationEnabled);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof LocationUserSettings)) {
+ return false;
+ }
+ LocationUserSettings that = (LocationUserSettings) o;
+ return mAdasGnssLocationEnabled == that.mAdasGnssLocationEnabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAdasGnssLocationEnabled);
+ }
+}
diff --git a/services/core/java/com/android/server/location/settings/SettingsStore.java b/services/core/java/com/android/server/location/settings/SettingsStore.java
new file mode 100644
index 000000000000..01338a3129da
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/SettingsStore.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 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.settings;
+
+import static com.android.server.location.LocationManagerService.TAG;
+import static com.android.server.location.settings.SettingsStore.VersionedSettings.VERSION_DOES_NOT_EXIST;
+
+import android.util.AtomicFile;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Function;
+
+/** Base class for read/write/versioning functionality for storing persistent settings to a file. */
+abstract class SettingsStore<T extends SettingsStore.VersionedSettings> {
+
+ interface VersionedSettings {
+ /** Represents that the settings do not exist. */
+ int VERSION_DOES_NOT_EXIST = Integer.MAX_VALUE;
+
+ /** Must always return a version number less than {@link #VERSION_DOES_NOT_EXIST}. */
+ int getVersion();
+ }
+
+ private final AtomicFile mFile;
+
+ @GuardedBy("this")
+ private boolean mInitialized;
+ @GuardedBy("this")
+ private T mCache;
+
+ protected SettingsStore(File file) {
+ mFile = new AtomicFile(file);
+ }
+
+ /**
+ * Must be implemented to read in a settings instance, and upgrade to the appropriate version
+ * where necessary. If the provided version is {@link VersionedSettings#VERSION_DOES_NOT_EXIST}
+ * then the DataInput will be empty, and the method should return a settings instance with all
+ * settings set to the default value.
+ */
+ protected abstract T read(int version, DataInput in) throws IOException;
+
+ /**
+ * Must be implemented to write the given settings to the given DataOutput.
+ */
+ protected abstract void write(DataOutput out, T settings) throws IOException;
+
+ /**
+ * Invoked when settings change, and while holding the internal lock. If used to invoke
+ * listeners, ensure they are not invoked while holding the lock (ie, asynchronously).
+ */
+ protected abstract void onChange(T oldSettings, T newSettings);
+
+ public final synchronized void initializeCache() {
+ if (!mInitialized) {
+ if (mFile.exists()) {
+ try (DataInputStream is = new DataInputStream(mFile.openRead())) {
+ mCache = read(is.readInt(), is);
+ Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+ } catch (IOException e) {
+ Log.e(TAG, "error reading location settings (" + mFile
+ + "), falling back to defaults", e);
+ }
+ }
+
+ if (mCache == null) {
+ try {
+ mCache = read(VERSION_DOES_NOT_EXIST,
+ new DataInputStream(new ByteArrayInputStream(new byte[0])));
+ Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ mInitialized = true;
+ }
+ }
+
+ public final synchronized T get() {
+ initializeCache();
+ return mCache;
+ }
+
+ public synchronized void update(Function<T, T> updater) {
+ initializeCache();
+
+ T oldSettings = mCache;
+ T newSettings = Objects.requireNonNull(updater.apply(oldSettings));
+ if (oldSettings.equals(newSettings)) {
+ return;
+ }
+
+ mCache = newSettings;
+ Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+
+ writeLazily(newSettings);
+
+ onChange(oldSettings, newSettings);
+ }
+
+ @VisibleForTesting
+ synchronized void flushFile() throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ BackgroundThread.getExecutor().execute(latch::countDown);
+ latch.await();
+ }
+
+ @VisibleForTesting
+ synchronized void deleteFile() throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ BackgroundThread.getExecutor().execute(() -> {
+ mFile.delete();
+ latch.countDown();
+ });
+ latch.await();
+ }
+
+ private void writeLazily(T settings) {
+ BackgroundThread.getExecutor().execute(() -> {
+ FileOutputStream os = null;
+ try {
+ os = mFile.startWrite();
+ DataOutputStream out = new DataOutputStream(os);
+ out.writeInt(settings.getVersion());
+ write(out, settings);
+ mFile.finishWrite(os);
+ } catch (IOException e) {
+ mFile.failWrite(os);
+ Log.e(TAG, "failure serializing location settings", e);
+ } catch (Throwable e) {
+ mFile.failWrite(os);
+ throw e;
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index bad7e5c27b7b..dab980a9e4b2 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -641,7 +641,7 @@ final class DefaultPermissionGrantPolicy {
// Cell Broadcast Receiver
grantSystemFixedPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackage(pm, Intents.SMS_CB_RECEIVED_ACTION, userId),
- userId, SMS_PERMISSIONS);
+ userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
// Carrier Provisioning Service
grantPermissionsToSystemPackage(pm,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index a391dbc1fa47..38e9d3ec34e3 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -5720,10 +5720,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
boolean fromDatasource, int attributedOp) {
// Now let's check the identity chain...
final int op = AppOpsManager.permissionToOpCode(permission);
- final int attributionChainId = (startDataDelivery)
- ? sAttributionChainIds.incrementAndGet()
- : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
-
+ final int attributionChainId =
+ getAttributionChainId(startDataDelivery, attributionSource);
AttributionSource current = attributionSource;
AttributionSource next = null;
@@ -5879,9 +5877,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
- final int attributionChainId = (startDataDelivery)
- ? sAttributionChainIds.incrementAndGet()
- : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+ final int attributionChainId =
+ getAttributionChainId(startDataDelivery, attributionSource);
AttributionSource current = attributionSource;
AttributionSource next = null;
@@ -6064,6 +6061,21 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
+ private static int getAttributionChainId(boolean startDataDelivery,
+ AttributionSource source) {
+ if (source == null || source.getNext() == null || !startDataDelivery) {
+ return AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+ }
+ int attributionChainId = sAttributionChainIds.incrementAndGet();
+
+ // handle overflow
+ if (attributionChainId < 0) {
+ attributionChainId = 0;
+ sAttributionChainIds.set(0);
+ }
+ return attributionChainId;
+ }
+
private static @Nullable String resolvePackageName(@NonNull Context context,
@NonNull AttributionSource attributionSource) {
if (attributionSource.getPackageName() != null) {
diff --git a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
index d439b94ec2fc..1d8c64bddd49 100644
--- a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
+++ b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
@@ -22,6 +22,7 @@ import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -59,6 +60,7 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
convertStepsToRamps(segments);
int newRepeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex);
newRepeatIndex = addRampDownToLoop(segments, newRepeatIndex);
+ newRepeatIndex = splitLongRampSegments(info, segments, newRepeatIndex);
return newRepeatIndex;
}
@@ -210,11 +212,70 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
return repeatIndex;
}
+ /**
+ * Split {@link RampSegment} entries that have duration longer than {@link
+ * VibratorInfo#getPwlePrimitiveDurationMax()}.
+ */
+ private int splitLongRampSegments(VibratorInfo info, List<VibrationEffectSegment> segments,
+ int repeatIndex) {
+ int maxDuration = info.getPwlePrimitiveDurationMax();
+ if (maxDuration <= 0) {
+ // No limit set to PWLE primitive duration.
+ return repeatIndex;
+ }
+
+ int segmentCount = segments.size();
+ for (int i = 0; i < segmentCount; i++) {
+ if (!(segments.get(i) instanceof RampSegment)) {
+ continue;
+ }
+ RampSegment ramp = (RampSegment) segments.get(i);
+ int splits = ((int) ramp.getDuration() + maxDuration - 1) / maxDuration;
+ if (splits <= 1) {
+ continue;
+ }
+ segments.remove(i);
+ segments.addAll(i, splitRampSegment(ramp, splits));
+ int addedSegments = splits - 1;
+ if (repeatIndex > i) {
+ repeatIndex += addedSegments;
+ }
+ i += addedSegments;
+ segmentCount += addedSegments;
+ }
+
+ return repeatIndex;
+ }
+
private static RampSegment apply(StepSegment segment) {
return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
}
+ private static List<RampSegment> splitRampSegment(RampSegment ramp, int splits) {
+ List<RampSegment> ramps = new ArrayList<>(splits);
+ long splitDuration = ramp.getDuration() / splits;
+ float previousAmplitude = ramp.getStartAmplitude();
+ float previousFrequency = ramp.getStartFrequency();
+ long accumulatedDuration = 0;
+
+ for (int i = 1; i < splits; i++) {
+ accumulatedDuration += splitDuration;
+ RampSegment rampSplit = new RampSegment(
+ previousAmplitude, interpolateAmplitude(ramp, accumulatedDuration),
+ previousFrequency, interpolateFrequency(ramp, accumulatedDuration),
+ (int) splitDuration);
+ ramps.add(rampSplit);
+ previousAmplitude = rampSplit.getEndAmplitude();
+ previousFrequency = rampSplit.getEndFrequency();
+ }
+
+ ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequency,
+ ramp.getEndFrequency(), (int) (ramp.getDuration() - accumulatedDuration)));
+
+ return ramps;
+ }
+
private static RampSegment createRampDown(float amplitude, float frequency, long duration) {
return new RampSegment(amplitude, /* endAmplitude= */ 0, frequency, frequency,
(int) duration);
@@ -244,4 +305,19 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
}
return false;
}
+
+ private static float interpolateAmplitude(RampSegment ramp, long duration) {
+ return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration,
+ ramp.getDuration());
+ }
+
+ private static float interpolateFrequency(RampSegment ramp, long duration) {
+ return interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), duration,
+ ramp.getDuration());
+ }
+
+ private static float interpolate(float start, float end, long duration, long totalDuration) {
+ float position = (float) duration / totalDuration;
+ return start + position * (end - start);
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b156e12a1ce8..30cd97e110ee 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -38,6 +38,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -1786,17 +1787,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* Evaluate the theme for a starting window.
+ * @param prev Previous activity which may have a starting window.
* @param originalTheme The original theme which read from activity or application.
* @param replaceTheme The replace theme which requested from starter.
* @return Resolved theme.
*/
- private int evaluateStartingWindowTheme(String pkg, int originalTheme, int replaceTheme) {
+ private int evaluateStartingWindowTheme(ActivityRecord prev, String pkg, int originalTheme,
+ int replaceTheme) {
// Skip if the package doesn't want a starting window.
- if (!validateStartingWindowTheme(pkg, originalTheme)) {
+ if (!validateStartingWindowTheme(prev, pkg, originalTheme)) {
return 0;
}
int selectedTheme = originalTheme;
- if (replaceTheme != 0 && validateStartingWindowTheme(pkg, replaceTheme)) {
+ if (replaceTheme != 0 && validateStartingWindowTheme(prev, pkg, replaceTheme)) {
// allow to replace theme
selectedTheme = replaceTheme;
}
@@ -1833,7 +1836,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return LAUNCH_SOURCE_TYPE_APPLICATION;
}
- private boolean validateStartingWindowTheme(String pkg, int theme) {
+ private boolean validateStartingWindowTheme(ActivityRecord prev, String pkg, int theme) {
// If this is a translucent window, then don't show a starting window -- the current
// effect (a full-screen opaque starting window that fades away to the real contents
// when it is ready) does not work for this.
@@ -1870,7 +1873,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
if (windowDisableStarting && !launchedFromSystemSurface()) {
- return false;
+ // Check if previous activity can transfer the starting window to this activity.
+ return prev != null && prev.getActivityType() == ACTIVITY_TYPE_STANDARD
+ && prev.mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_IDLE
+ && (prev.mStartingData != null
+ || (prev.mStartingWindow != null && prev.mStartingSurface != null));
}
return true;
}
@@ -6276,15 +6283,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mSplashScreenStyleEmpty = shouldUseEmptySplashScreen(sourceRecord);
- final int resolvedTheme = evaluateStartingWindowTheme(packageName, theme,
+ final int resolvedTheme = evaluateStartingWindowTheme(prev, packageName, theme,
splashScreenTheme);
+ final boolean activityCreated =
+ mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal();
+ // If this activity is just created and all activities below are finish, treat this
+ // scenario as warm launch.
+ final boolean newSingleActivity = !newTask && !activityCreated
+ && task.getActivity((r) -> !r.finishing && r != this) == null;
+
final boolean shown = addStartingWindow(packageName, resolvedTheme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
- prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
- allowTaskSnapshot(),
- mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
- mSplashScreenStyleEmpty);
+ prev != null ? prev.appToken : null,
+ newTask || newSingleActivity, taskSwitch, isProcessRunning(),
+ allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);
if (shown) {
mStartingWindowState = STARTING_WINDOW_SHOWN;
}
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 0ec01422474a..2ea043a5c623 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -75,10 +75,18 @@ public final class CompatModePackages {
* CompatModePackages#DOWNSCALED is the gatekeeper of all per-app buffer downscaling
* changes. Disabling this change will prevent the following scaling factors from working:
* CompatModePackages#DOWNSCALE_90
+ * CompatModePackages#DOWNSCALE_85
* CompatModePackages#DOWNSCALE_80
+ * CompatModePackages#DOWNSCALE_75
* CompatModePackages#DOWNSCALE_70
+ * CompatModePackages#DOWNSCALE_65
* CompatModePackages#DOWNSCALE_60
+ * CompatModePackages#DOWNSCALE_55
* CompatModePackages#DOWNSCALE_50
+ * CompatModePackages#DOWNSCALE_45
+ * CompatModePackages#DOWNSCALE_40
+ * CompatModePackages#DOWNSCALE_35
+ * CompatModePackages#DOWNSCALE_30
*
* If CompatModePackages#DOWNSCALED is enabled for an app package, then the app will be forcibly
* resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were enabled.
@@ -100,6 +108,16 @@ public final class CompatModePackages {
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_85 for a package will force the app to assume it's
+ * running on a display with 85% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_85 = 189969734L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
* CompatModePackages#DOWNSCALE_80 for a package will force the app to assume it's
* running on a display with 80% the vertical and horizontal resolution of the real display.
*/
@@ -110,6 +128,16 @@ public final class CompatModePackages {
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_75 for a package will force the app to assume it's
+ * running on a display with 75% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_75 = 189969779L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
* CompatModePackages#DOWNSCALE_70 for a package will force the app to assume it's
* running on a display with 70% the vertical and horizontal resolution of the real display.
*/
@@ -120,6 +148,16 @@ public final class CompatModePackages {
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_65 for a package will force the app to assume it's
+ * running on a display with 65% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_65 = 189969744L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
* CompatModePackages#DOWNSCALE_60 for a package will force the app to assume it's
* running on a display with 60% the vertical and horizontal resolution of the real display.
*/
@@ -130,6 +168,16 @@ public final class CompatModePackages {
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_55 for a package will force the app to assume it's
+ * running on a display with 55% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_55 = 189970036L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
* CompatModePackages#DOWNSCALE_50 for a package will force the app to assume it's
* running on a display with 50% vertical and horizontal resolution of the real display.
*/
@@ -139,6 +187,46 @@ public final class CompatModePackages {
public static final long DOWNSCALE_50 = 176926741L;
/**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_45 for a package will force the app to assume it's
+ * running on a display with 45% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_45 = 189969782L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_40 for a package will force the app to assume it's
+ * running on a display with 40% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_40 = 189970038L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_35 for a package will force the app to assume it's
+ * running on a display with 35% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_35 = 189969749L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_30 for a package will force the app to assume it's
+ * running on a display with 30% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_30 = 189970040L;
+
+ /**
* On Android TV applications that target pre-S are not expecting to receive a Window larger
* than 1080p, so if needed we are downscaling their Windows to 1080p.
* However, applications that target S and greater release version are expected to be able to
@@ -291,18 +379,42 @@ public final class CompatModePackages {
if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) {
return 1f / 0.9f;
}
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_85, packageName, userHandle)) {
+ return 1f / 0.85f;
+ }
if (CompatChanges.isChangeEnabled(DOWNSCALE_80, packageName, userHandle)) {
return 1f / 0.8f;
}
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_75, packageName, userHandle)) {
+ return 1f / 0.75f;
+ }
if (CompatChanges.isChangeEnabled(DOWNSCALE_70, packageName, userHandle)) {
return 1f / 0.7f;
}
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_65, packageName, userHandle)) {
+ return 1f / 0.65f;
+ }
if (CompatChanges.isChangeEnabled(DOWNSCALE_60, packageName, userHandle)) {
return 1f / 0.6f;
}
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_55, packageName, userHandle)) {
+ return 1f / 0.55f;
+ }
if (CompatChanges.isChangeEnabled(DOWNSCALE_50, packageName, userHandle)) {
return 1f / 0.5f;
}
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_45, packageName, userHandle)) {
+ return 1f / 0.45f;
+ }
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_40, packageName, userHandle)) {
+ return 1f / 0.4f;
+ }
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_35, packageName, userHandle)) {
+ return 1f / 0.35f;
+ }
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_30, packageName, userHandle)) {
+ return 1f / 0.3f;
+ }
}
if (mService.mHasLeanbackFeature) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
index b480f24fb371..5e219a25ed9a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
@@ -18,6 +18,7 @@ package com.android.server.location.gnss;
import static com.google.common.truth.Truth.assertThat;
+import android.content.Context;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -49,6 +50,7 @@ public class GnssGeofenceProxyTest {
private static final int NOTIFICATION_RESPONSIVENESS = 0;
private static final int UNKNOWN_TIMER = 0;
+ private @Mock Context mContext;
private @Mock GnssConfiguration mMockConfiguration;
private @Mock GnssNative.GeofenceCallbacks mGeofenceCallbacks;
@@ -63,7 +65,7 @@ public class GnssGeofenceProxyTest {
GnssNative.setGnssHalForTest(mFakeHal);
GnssNative gnssNative = Objects.requireNonNull(
- GnssNative.create(new TestInjector(), mMockConfiguration));
+ GnssNative.create(new TestInjector(mContext), mMockConfiguration));
gnssNative.setGeofenceCallbacks(mGeofenceCallbacks);
mTestProvider = new GnssGeofenceProxy(gnssNative);
gnssNative.register();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
index 3d0378170c94..d728451d92b9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
@@ -29,9 +29,10 @@ import java.util.HashMap;
public class FakeAppOpsHelper extends AppOpsHelper {
private static class AppOp {
- private boolean mAllowed = true;
- private boolean mStarted = false;
- private int mNoteCount = 0;
+ AppOp() {}
+ boolean mAllowed = true;
+ boolean mStarted = false;
+ int mNoteCount = 0;
}
private final HashMap<String, SparseArray<AppOp>> mAppOps;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
index f1099f0e8184..cd70020f5c28 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
@@ -29,8 +29,8 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/**
- * Version of AppOpsHelper for testing. Settings are initialized to reasonable defaults (location is
- * enabled by default).
+ * Version of SettingsHelper for testing. Settings are initialized to reasonable defaults (location
+ * is enabled by default).
*/
public class FakeSettingsHelper extends SettingsHelper {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index ae70dadba041..bd24cfd78a2c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -16,9 +16,14 @@
package com.android.server.location.injector;
+import android.content.Context;
+
+import com.android.server.location.settings.FakeLocationSettings;
+
public class TestInjector implements Injector {
private final FakeUserInfoHelper mUserInfoHelper;
+ private final FakeLocationSettings mLocationSettings;
private final FakeAlarmHelper mAlarmHelper;
private final FakeAppOpsHelper mAppOpsHelper;
private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
@@ -32,8 +37,9 @@ public class TestInjector implements Injector {
private final FakeEmergencyHelper mEmergencyHelper;
private final LocationUsageLogger mLocationUsageLogger;
- public TestInjector() {
+ public TestInjector(Context context) {
mUserInfoHelper = new FakeUserInfoHelper();
+ mLocationSettings = new FakeLocationSettings(context);
mAlarmHelper = new FakeAlarmHelper();
mAppOpsHelper = new FakeAppOpsHelper();
mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
@@ -54,6 +60,11 @@ public class TestInjector implements Injector {
}
@Override
+ public FakeLocationSettings getLocationSettings() {
+ return mLocationSettings;
+ }
+
+ @Override
public FakeAlarmHelper getAlarmHelper() {
return mAlarmHelper;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 6bc3b6041070..f703e2e59181 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -19,6 +19,8 @@ package com.android.server.location.provider;
import static android.app.AppOpsManager.OP_FINE_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.FEATURE_AUTOMOTIVE;
+import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationRequest.PASSIVE_INTERVAL;
import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
@@ -56,6 +58,8 @@ import static org.mockito.MockitoAnnotations.initMocks;
import static org.testng.Assert.assertThrows;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.LastLocationRequest;
@@ -82,6 +86,7 @@ import android.util.Log;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.location.injector.FakeUserInfoHelper;
@@ -139,6 +144,10 @@ public class LocationProviderManagerTest {
@Mock
private Context mContext;
@Mock
+ private Resources mResources;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
private PowerManager mPowerManager;
@Mock
private PowerManager.WakeLock mWakeLock;
@@ -161,20 +170,28 @@ public class LocationProviderManagerTest {
LocalServices.addService(LocationManagerInternal.class, mInternal);
doReturn("android").when(mContext).getPackageName();
+ doReturn(mResources).when(mContext).getResources();
+ doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
- mInjector = new TestInjector();
+ mInjector = new TestInjector(mContext);
mInjector.getUserInfoHelper().startUser(OTHER_USER);
mPassive = new PassiveLocationProviderManager(mContext, mInjector);
mPassive.startManager(null);
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
+ createManager(NAME);
+ }
+
+ private void createManager(String name) {
+ mStateChangedListener = mock(LocationProviderManager.StateChangedListener.class);
+
mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY);
mProvider.setProviderAllowed(true);
- mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
+ mManager = new LocationProviderManager(mContext, mInjector, name, mPassive);
mManager.startManager(mStateChangedListener);
mManager.setRealProvider(mProvider);
}
@@ -1017,6 +1034,95 @@ public class LocationProviderManagerTest {
}
@Test
+ public void testProviderRequest_AdasGnssBypass() {
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ createManager(GPS_PROVIDER);
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(5)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(1)
+ .setAdasGnssBypass(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
+ }
+
+ @Test
+ public void testProviderRequest_AdasGnssBypass_ProviderDisabled() {
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ createManager(GPS_PROVIDER);
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(1)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(5)
+ .setAdasGnssBypass(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+ mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
+ }
+
+ @Test
+ public void testProviderRequest_AdasGnssBypass_ProviderDisabled_AdasDisabled() {
+ mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
+ new PackageTagsList.Builder().add(
+ IDENTITY.getPackageName()).build());
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ createManager(GPS_PROVIDER);
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(5)
+ .setLocationSettingsIgnored(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(1)
+ .setAdasGnssBypass(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+ mInjector.getLocationSettings().updateUserSettings(IDENTITY.getUserId(),
+ settings -> settings.withAdasGnssLocationEnabled(false));
+ mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
+ }
+
+ @Test
public void testProviderRequest_BatterySaver_ScreenOnOff() {
mInjector.getLocationPowerSaveModeHelper().setLocationPowerSaveMode(
LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 04e0151e619a..63996f0e021c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
+import android.content.Context;
import android.location.Location;
import android.location.LocationResult;
import android.location.provider.ProviderRequest;
@@ -58,6 +59,7 @@ public class StationaryThrottlingLocationProviderTest {
private TestInjector mInjector;
private FakeProvider mDelegateProvider;
+ private @Mock Context mContext;
private @Mock AbstractLocationProvider.Listener mListener;
private @Mock FakeProvider.FakeProviderInterface mDelegate;
@@ -72,7 +74,7 @@ public class StationaryThrottlingLocationProviderTest {
mRandom = new Random(seed);
- mInjector = new TestInjector();
+ mInjector = new TestInjector(mContext);
mDelegateProvider = new FakeProvider(mDelegate);
mProvider = new StationaryThrottlingLocationProvider("test_provider", mInjector,
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java b/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java
new file mode 100644
index 000000000000..4d46abaa6733
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 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.settings;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import java.io.File;
+
+public class FakeLocationSettings extends LocationSettings {
+
+ public FakeLocationSettings(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected File getUserSettingsDir(int userId) {
+ return ApplicationProvider.getApplicationContext().getCacheDir();
+ }
+
+ @Override
+ protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) {
+ return new FakeLocationUserSettingsStore(userId, file);
+ }
+
+ private class FakeLocationUserSettingsStore extends LocationUserSettingsStore {
+
+ FakeLocationUserSettingsStore(int userId, File file) {
+ super(userId, file);
+ }
+
+ @Override
+ protected void onChange(LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ fireListeners(mUserId, oldSettings, newSettings);
+ }
+ }
+}
+
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java b/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java
new file mode 100644
index 000000000000..4b6c79b954cd
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2021 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.settings;
+
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
+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.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.io.File;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LocationSettingsTest {
+
+ private @Mock Context mContext;
+ private @Mock Resources mResources;
+ private @Mock PackageManager mPackageManager;
+
+ private LocationSettings mLocationSettings;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+
+ doReturn(mResources).when(mContext).getResources();
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+
+ resetLocationSettings();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mLocationSettings.deleteFiles();
+ }
+
+ private void resetLocationSettings() {
+ mLocationSettings = new LocationSettings(mContext) {
+ @Override
+ protected File getUserSettingsDir(int userId) {
+ return ApplicationProvider.getApplicationContext().getCacheDir();
+ }
+ };
+ }
+
+ @Test
+ public void testLoadDefaults() {
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ assertThat(mLocationSettings.getUserSettings(2).isAdasGnssLocationEnabled()).isFalse();
+ }
+
+ @Test
+ public void testUpdate() {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(false));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+ }
+
+ @Test
+ public void testSerialization() throws Exception {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+ mLocationSettings.flushFiles();
+ resetLocationSettings();
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+ }
+
+ @Test
+ public void testListeners() {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ LocationSettings.LocationUserSettingsListener listener = mock(
+ LocationSettings.LocationUserSettingsListener.class);
+
+ mLocationSettings.registerLocationUserSettingsListener(listener);
+
+ ArgumentCaptor<LocationUserSettings> oldCaptor = ArgumentCaptor.forClass(
+ LocationUserSettings.class);
+ ArgumentCaptor<LocationUserSettings> newCaptor = ArgumentCaptor.forClass(
+ LocationUserSettings.class);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ verify(listener, timeout(500).times(1)).onLocationUserSettingsChanged(eq(1),
+ oldCaptor.capture(), newCaptor.capture());
+ assertThat(oldCaptor.getValue().isAdasGnssLocationEnabled()).isFalse();
+ assertThat(newCaptor.getValue().isAdasGnssLocationEnabled()).isTrue();
+
+ oldCaptor = ArgumentCaptor.forClass(LocationUserSettings.class);
+ newCaptor = ArgumentCaptor.forClass(LocationUserSettings.class);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(false));
+ verify(listener, timeout(500).times(2)).onLocationUserSettingsChanged(eq(1),
+ oldCaptor.capture(), newCaptor.capture());
+ assertThat(oldCaptor.getValue().isAdasGnssLocationEnabled()).isTrue();
+ assertThat(newCaptor.getValue().isAdasGnssLocationEnabled()).isFalse();
+
+ mLocationSettings.unregisterLocationUserSettingsListener(listener);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ verify(listener, after(500).times(2)).onLocationUserSettingsChanged(anyInt(), any(), any());
+ }
+
+ @Test
+ public void testNonAutomotive() {
+ doReturn(false).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ LocationSettings.LocationUserSettingsListener listener = mock(
+ LocationSettings.LocationUserSettingsListener.class);
+ mLocationSettings.registerLocationUserSettingsListener(listener);
+
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+ verify(listener, after(500).never()).onLocationUserSettingsChanged(anyInt(), any(), any());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
index f4eb2ded5a9d..32988efbab2c 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
@@ -68,6 +68,38 @@ public class StepToRampAdapterTest {
}
@Test
+ public void testRampSegments_withPwleDurationLimit_splitsLongRamps() {
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
+ /* startFrequency= */ 0, /* endFrequency= */ -1, /* duration= */ 25),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
+ /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5)));
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0.32f,
+ /* startFrequency= */ 0, /* endFrequency= */ -0.32f, /* duration= */ 8),
+ new RampSegment(/* startAmplitude= */ 0.32f, /* endAmplitude= */ 0.64f,
+ /* startFrequency= */ -0.32f, /* endFrequency= */ -0.64f,
+ /* duration= */ 8),
+ new RampSegment(/* startAmplitude= */ 0.64f, /* endAmplitude= */ 1,
+ /* startFrequency= */ -0.64f, /* endFrequency= */ -1, /* duration= */ 9),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
+ /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5));
+
+ VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
+ .setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
+ .setPwlePrimitiveDurationMax(10)
+ .build();
+
+ // Update repeat index to skip the ramp splits.
+ assertEquals(4, mAdapter.apply(segments, 2, vibratorInfo));
+ assertEquals(expectedSegments, segments);
+ }
+
+ @Test
public void testStepAndRampSegments_withoutPwleCapability_keepsListUnchanged() {
mAdapter = new StepToRampAdapter(50);
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 4ce78aa4d8c1..dc338ae0fdc7 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -20,6 +20,8 @@ import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -70,6 +72,14 @@ public class VcnGatewayConnectionConfigTest {
public static final String GATEWAY_CONNECTION_NAME_PREFIX = "gatewayConnectionName-";
private static int sGatewayConnectionConfigCount = 0;
+ private static VcnGatewayConnectionConfig buildTestConfig(
+ String gatewayConnectionName, IkeTunnelConnectionParams tunnelConnectionParams) {
+ return buildTestConfigWithExposedCaps(
+ new VcnGatewayConnectionConfig.Builder(
+ gatewayConnectionName, tunnelConnectionParams),
+ EXPOSED_CAPS);
+ }
+
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
@@ -83,10 +93,9 @@ public class VcnGatewayConnectionConfigTest {
TUNNEL_CONNECTION_PARAMS);
}
- // Public for use in VcnGatewayConnectionTest
- public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
- final VcnGatewayConnectionConfig.Builder builder =
- newBuilder().setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
+ private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(
+ VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) {
+ builder.setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
@@ -95,6 +104,11 @@ public class VcnGatewayConnectionConfigTest {
return builder.build();
}
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
+ return buildTestConfigWithExposedCaps(newBuilder(), exposedCaps);
+ }
+
@Test
public void testBuilderRequiresNonNullGatewayConnectionName() {
try {
@@ -193,4 +207,46 @@ public class VcnGatewayConnectionConfigTest {
assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
}
+
+ private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) {
+ final IkeSessionParams ikeParams =
+ IkeSessionParamsUtilsTest.createBuilderMinimum()
+ .setAuthPsk(ikePsk.getBytes())
+ .build();
+ return TunnelConnectionParamsUtilsTest.buildTestParams(ikeParams);
+ }
+
+ @Test
+ public void testTunnelConnectionParamsEquals() throws Exception {
+ final String connectionName = "testTunnelConnectionParamsEquals.connectionName";
+ final String psk = "testTunnelConnectionParamsEquals.psk";
+
+ final IkeTunnelConnectionParams tunnelParams = buildTunnelConnectionParams(psk);
+ final VcnGatewayConnectionConfig config = buildTestConfig(connectionName, tunnelParams);
+
+ final IkeTunnelConnectionParams anotherTunnelParams = buildTunnelConnectionParams(psk);
+ final VcnGatewayConnectionConfig anotherConfig =
+ buildTestConfig(connectionName, anotherTunnelParams);
+
+ assertNotSame(tunnelParams, anotherTunnelParams);
+ assertEquals(tunnelParams, anotherTunnelParams);
+ assertEquals(config, anotherConfig);
+ }
+
+ @Test
+ public void testTunnelConnectionParamsNotEquals() throws Exception {
+ final String connectionName = "testTunnelConnectionParamsNotEquals.connectionName";
+
+ final IkeTunnelConnectionParams tunnelParams =
+ buildTunnelConnectionParams("testTunnelConnectionParamsNotEquals.pskA");
+ final VcnGatewayConnectionConfig config = buildTestConfig(connectionName, tunnelParams);
+
+ final IkeTunnelConnectionParams anotherTunnelParams =
+ buildTunnelConnectionParams("testTunnelConnectionParamsNotEquals.pskB");
+ final VcnGatewayConnectionConfig anotherConfig =
+ buildTestConfig(connectionName, anotherTunnelParams);
+
+ assertNotEquals(tunnelParams, anotherTunnelParams);
+ assertNotEquals(config, anotherConfig);
+ }
}