diff options
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); + } } |