Merge "Fix PendingIntent hijacking for adb notifications." into rvc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index f06f279..270c160 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -76,9 +76,14 @@
name: "metalava-non-updatable-api-stubs-default",
defaults: ["metalava-base-api-stubs-default"],
sdk_version: "core_platform",
- libs: ["framework-all"],
+ // There are a few classes from modules used as type arguments that
+ // need to be resolved by metalava. For now, we can use a previously
+ // finalized stub library to resolve them. If a new class gets added,
+ // this may be need to be revisited to use a manually maintained stub
+ // library with empty classes in order to resolve those references.
+ libs: ["sdk_system_29_android"],
aidl: {
- local_include_dirs: ["apex/media/framework/java"],
+ local_include_dirs: ["apex/media/aidl/stable"],
},
}
@@ -293,7 +298,7 @@
name: "android_module_lib_stubs_current",
srcs: [ ":module-lib-api-stubs-docs" ],
defaults: ["android_defaults_stubs_current"],
- libs: ["android_system_stubs_current"],
+ libs: ["sdk_system_29_android"],
}
/////////////////////////////////////////////////////////////////////
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 073fddf..202decc 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -21,7 +21,6 @@
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.media.MediaCodec.CryptoInfo;
-import android.net.Uri;
import android.text.TextUtils;
import android.util.Pair;
import android.util.SparseArray;
@@ -52,9 +51,6 @@
import com.google.android.exoplayer2.extractor.ts.TsExtractor;
import com.google.android.exoplayer2.extractor.wav.WavExtractor;
import com.google.android.exoplayer2.upstream.DataReader;
-import com.google.android.exoplayer2.upstream.DataSource;
-import com.google.android.exoplayer2.upstream.DataSpec;
-import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.ColorInfo;
@@ -847,7 +843,7 @@
private final OutputConsumer mOutputConsumer;
private final String[] mParserNamesPool;
private final PositionHolder mPositionHolder;
- private final InputReadingDataSource mDataSource;
+ private final InputReadingDataReader mExoDataReader;
private final DataReaderAdapter mScratchDataReaderAdapter;
private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
private String mParserName;
@@ -950,11 +946,11 @@
// clearBuffers() method, or similar.
mExtractorInput =
new DefaultExtractorInput(
- mDataSource,
+ mExoDataReader,
seekableInputReader.getPosition(),
seekableInputReader.getLength());
}
- mDataSource.mInputReader = seekableInputReader;
+ mExoDataReader.mInputReader = seekableInputReader;
// TODO: Apply parameters when creating extractor instances.
if (mExtractor == null) {
@@ -1046,7 +1042,7 @@
mParserNamesPool = parserNamesPool;
mParserName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0];
mPositionHolder = new PositionHolder();
- mDataSource = new InputReadingDataSource();
+ mExoDataReader = new InputReadingDataReader();
removePendingSeek();
mScratchDataReaderAdapter = new DataReaderAdapter();
mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter();
@@ -1181,39 +1177,14 @@
// Private classes.
- private static final class InputReadingDataSource implements DataSource {
+ private static final class InputReadingDataReader implements DataReader {
public InputReader mInputReader;
@Override
- public void addTransferListener(TransferListener transferListener) {
- // Do nothing.
- }
-
- @Override
- public long open(DataSpec dataSpec) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public int read(byte[] buffer, int offset, int readLength) throws IOException {
return mInputReader.read(buffer, offset, readLength);
}
-
- @Override
- public Uri getUri() {
- return null;
- }
-
- @Override
- public Map<String, List<String>> getResponseHeaders() {
- return null;
- }
-
- @Override
- public void close() {
- throw new UnsupportedOperationException();
- }
}
private final class ExtractorOutputAdapter implements ExtractorOutput {
diff --git a/api/test-current.txt b/api/test-current.txt
index 0dff41b..8e8c8c4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -107,6 +107,7 @@
}
public class ActivityOptions {
+ method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method public static void setExitTransitionTimeout(long);
method public void setLaunchActivityType(int);
method public void setLaunchTaskId(int);
@@ -115,6 +116,14 @@
method public void setTaskOverlay(boolean, boolean);
}
+ public static interface ActivityOptions.OnAnimationFinishedListener {
+ method public void onAnimationFinished();
+ }
+
+ public static interface ActivityOptions.OnAnimationStartedListener {
+ method public void onAnimationStarted();
+ }
+
public class ActivityTaskManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void clearLaunchParamsForPackages(java.util.List<java.lang.String>);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public String listAllStacks();
@@ -2263,7 +2272,11 @@
public class Environment {
method public static java.io.File buildPath(java.io.File, java.lang.String...);
+ method @NonNull public static java.io.File getOdmDirectory();
+ method @NonNull public static java.io.File getOemDirectory();
method @NonNull public static java.io.File getProductDirectory();
+ method @NonNull public static java.io.File getSystemExtDirectory();
+ method @NonNull public static java.io.File getVendorDirectory();
}
public final class FileUtils {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 7fd02112..0129aab 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
@@ -51,6 +52,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.window.WindowContainerToken;
import java.util.ArrayList;
@@ -184,6 +186,14 @@
private static final String KEY_CALLER_DISPLAY_ID = "android.activity.callerDisplayId";
/**
+ * The task display area token the activity should be launched into.
+ * @see #setLaunchTaskDisplayArea(WindowContainerToken)
+ * @hide
+ */
+ private static final String KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN =
+ "android.activity.launchTaskDisplayAreaToken";
+
+ /**
* The windowing mode the activity should be launched into.
* @hide
*/
@@ -334,6 +344,7 @@
private PendingIntent mUsageTimeReport;
private int mLaunchDisplayId = INVALID_DISPLAY;
private int mCallerDisplayId = INVALID_DISPLAY;
+ private WindowContainerToken mLaunchTaskDisplayArea;
@WindowConfiguration.WindowingMode
private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED;
@WindowConfiguration.ActivityType
@@ -369,7 +380,7 @@
*/
public static ActivityOptions makeCustomAnimation(Context context,
int enterResId, int exitResId) {
- return makeCustomAnimation(context, enterResId, exitResId, null, null);
+ return makeCustomAnimation(context, enterResId, exitResId, null, null, null);
}
/**
@@ -404,6 +415,38 @@
}
/**
+ * Create an ActivityOptions specifying a custom animation to run when
+ * the activity is displayed.
+ *
+ * @param context Who is defining this. This is the application that the
+ * animation resources will be loaded from.
+ * @param enterResId A resource ID of the animation resource to use for
+ * the incoming activity. Use 0 for no animation.
+ * @param exitResId A resource ID of the animation resource to use for
+ * the outgoing activity. Use 0 for no animation.
+ * @param handler If <var>listener</var> is non-null this must be a valid
+ * Handler on which to dispatch the callback; otherwise it should be null.
+ * @param startedListener Optional OnAnimationStartedListener to find out when the
+ * requested animation has started running. If for some reason the animation
+ * is not executed, the callback will happen immediately.
+ * @param finishedListener Optional OnAnimationFinishedListener when the animation
+ * has finished running.
+ * @return Returns a new ActivityOptions object that you can use to
+ * supply these options as the options Bundle when starting an activity.
+ * @hide
+ */
+ @TestApi
+ public static @NonNull ActivityOptions makeCustomAnimation(@NonNull Context context,
+ int enterResId, int exitResId, @Nullable Handler handler,
+ @Nullable OnAnimationStartedListener startedListener,
+ @Nullable OnAnimationFinishedListener finishedListener) {
+ ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler,
+ startedListener);
+ opts.setOnAnimationFinishedListener(handler, finishedListener);
+ return opts;
+ }
+
+ /**
* Creates an ActivityOptions specifying a custom animation to run in place on an existing
* activity.
*
@@ -448,6 +491,7 @@
* to find out when the given animation has started running.
* @hide
*/
+ @TestApi
public interface OnAnimationStartedListener {
void onAnimationStarted();
}
@@ -474,6 +518,7 @@
* to find out when the given animation has drawn its last frame.
* @hide
*/
+ @TestApi
public interface OnAnimationFinishedListener {
void onAnimationFinished();
}
@@ -974,6 +1019,7 @@
mLockTaskMode = opts.getBoolean(KEY_LOCK_TASK_MODE, false);
mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY);
mCallerDisplayId = opts.getInt(KEY_CALLER_DISPLAY_ID, INVALID_DISPLAY);
+ mLaunchTaskDisplayArea = opts.getParcelable(KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN);
mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED);
mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED);
mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
@@ -1089,7 +1135,7 @@
}
/** @hide */
- public IRemoteCallback getOnAnimationStartListener() {
+ public IRemoteCallback getAnimationStartedListener() {
return mAnimationStartedListener;
}
@@ -1245,6 +1291,18 @@
}
/** @hide */
+ public WindowContainerToken getLaunchTaskDisplayArea() {
+ return mLaunchTaskDisplayArea;
+ }
+
+ /** @hide */
+ public ActivityOptions setLaunchTaskDisplayArea(
+ WindowContainerToken windowContainerToken) {
+ mLaunchTaskDisplayArea = windowContainerToken;
+ return this;
+ }
+
+ /** @hide */
public int getLaunchWindowingMode() {
return mLaunchWindowingMode;
}
@@ -1568,6 +1626,9 @@
if (mCallerDisplayId != INVALID_DISPLAY) {
b.putInt(KEY_CALLER_DISPLAY_ID, mCallerDisplayId);
}
+ if (mLaunchTaskDisplayArea != null) {
+ b.putParcelable(KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN, mLaunchTaskDisplayArea);
+ }
if (mLaunchWindowingMode != WINDOWING_MODE_UNDEFINED) {
b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index ce0d04b..a24a5b78 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -22,6 +22,7 @@
import com.android.internal.annotations.GuardedBy;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
@@ -168,6 +169,17 @@
private static final boolean ENABLE = true;
private static final boolean VERIFY = false;
+ private static final Object sCorkLock = new Object();
+
+ /**
+ * A map of cache keys that we've "corked". (The values are counts.) When a cache key is
+ * corked, we skip the cache invalidate when the cache key is in the unset state --- that
+ * is, when a cache key is corked, an invalidation does not enable the cache if somebody
+ * else hasn't disabled it.
+ */
+ @GuardedBy("sCorkLock")
+ private static final HashMap<String, Integer> sCorks = new HashMap<>();
+
private final Object mLock = new Object();
/**
@@ -421,6 +433,25 @@
* @param name Name of the cache-key property to invalidate
*/
public static void invalidateCache(@NonNull String name) {
+ // Take the cork lock so invalidateCache() racing against corkInvalidations() doesn't
+ // clobber a cork-written NONCE_UNSET with a cache key we compute before the cork.
+ // The property service is single-threaded anyway, so we don't lose any concurrency by
+ // taking the cork lock around cache invalidations. If we see contention on this lock,
+ // we're invalidating too often.
+ synchronized (sCorkLock) {
+ Integer numberCorks = sCorks.get(name);
+ if (numberCorks != null && numberCorks > 0) {
+ if (DEBUG) {
+ Log.d(TAG, "ignoring invalidation due to cork: " + name);
+ }
+ return;
+ }
+ invalidateCacheLocked(name);
+ }
+ }
+
+ @GuardedBy("sCorkLock")
+ private static void invalidateCacheLocked(@NonNull String name) {
// There's no race here: we don't require that values strictly increase, but instead
// only that each is unique in a single runtime-restart session.
final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
@@ -430,6 +461,7 @@
}
return;
}
+
long newValue;
do {
newValue = NoPreloadHolder.next();
@@ -445,6 +477,67 @@
SystemProperties.set(name, newValueString);
}
+ /**
+ * Temporarily put the cache in the uninitialized state and prevent invalidations from
+ * moving it out of that state: useful in cases where we want to avoid the overhead of a
+ * large number of cache invalidations in a short time. While the cache is corked, clients
+ * bypass the cache and talk to backing services directly. This property makes corking
+ * correctness-preserving even if corked outside the lock that controls access to the
+ * cache's backing service.
+ *
+ * corkInvalidations() and uncorkInvalidations() must be called in pairs.
+ *
+ * @param name Name of the cache-key property to cork
+ */
+ public static void corkInvalidations(@NonNull String name) {
+ synchronized (sCorkLock) {
+ int numberCorks = sCorks.getOrDefault(name, 0);
+ // If we're the first ones to cork this cache, set the cache to the unset state so
+ // existing caches talk directly to their services while we've corked updates.
+ // Make sure we don't clobber a disabled cache value.
+
+ // TODO(dancol): we can skip this property write and leave the cache enabled if the
+ // caller promises not to make observable changes to the cache backing state before
+ // uncorking the cache, e.g., by holding a read lock across the cork-uncork pair.
+ // Implement this more dangerous mode of operation if necessary.
+ if (numberCorks == 0) {
+ final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
+ if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) {
+ SystemProperties.set(name, Long.toString(NONCE_UNSET));
+ }
+ }
+ sCorks.put(name, numberCorks + 1);
+ if (DEBUG) {
+ Log.d(TAG, "corked: " + name);
+ }
+ }
+ }
+
+ /**
+ * Undo the effect of a cork, allowing cache invalidations to proceed normally.
+ * Removing the last cork on a cache name invalidates the cache by side effect,
+ * transitioning it to normal operation (unless explicitly disabled system-wide).
+ *
+ * @param name Name of the cache-key property to uncork
+ */
+ public static void uncorkInvalidations(@NonNull String name) {
+ synchronized (sCorkLock) {
+ int numberCorks = sCorks.getOrDefault(name, 0);
+ if (numberCorks < 1) {
+ throw new AssertionError("cork underflow: " + name);
+ }
+ if (numberCorks == 1) {
+ sCorks.remove(name);
+ invalidateCacheLocked(name);
+ if (DEBUG) {
+ Log.d(TAG, "uncorked: " + name);
+ }
+ } else {
+ sCorks.put(name, numberCorks - 1);
+ }
+ }
+ }
+
protected Result maybeCheckConsistency(Query query, Result proposedResult) {
if (VERIFY) {
Result resultToCompare = recompute(query);
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 5f8c4f5..c7355dd 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -235,6 +235,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static @NonNull File getOemDirectory() {
return DIR_OEM_ROOT;
}
@@ -246,6 +247,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static @NonNull File getOdmDirectory() {
return DIR_ODM_ROOT;
}
@@ -256,6 +258,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static @NonNull File getVendorDirectory() {
return DIR_VENDOR_ROOT;
}
@@ -294,6 +297,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static @NonNull File getSystemExtDirectory() {
return DIR_SYSTEM_EXT_ROOT;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4be9e1a..e933f18a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3285,7 +3285,7 @@
/**
* Applies a tint to the compound drawables. Does not modify the
- * current tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+ * current tint mode, which is {@link BlendMode#SRC_IN} by default.
* <p>
* Subsequent calls to
* {@link #setCompoundDrawables(Drawable, Drawable, Drawable, Drawable)}
diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
index b814e3f..7c9c51c 100644
--- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
+++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
@@ -79,21 +79,6 @@
}
/**
- * Annotation for different shortcut target.
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- TargetType.ACCESSIBILITY_SERVICE,
- TargetType.ACCESSIBILITY_ACTIVITY,
- TargetType.WHITE_LISTING,
- })
- public @interface TargetType {
- int ACCESSIBILITY_SERVICE = 0;
- int ACCESSIBILITY_ACTIVITY = 1;
- int WHITE_LISTING = 2;
- }
-
- /**
* Annotation for different shortcut menu mode.
*
* {@code LAUNCH} for clicking list item to trigger the service callback.
@@ -108,30 +93,4 @@
int LAUNCH = 0;
int EDIT = 1;
}
-
- /**
- * Annotation for align the element index of white listing feature
- * {@code WHITE_LISTING_FEATURES}.
- *
- * {@code COMPONENT_ID} is to get the service component name.
- * {@code LABEL_ID} is to get the service label text.
- * {@code ICON_ID} is to get the service icon.
- * {@code FRAGMENT_TYPE} is to get the service fragment type.
- * {@code SETTINGS_KEY} is to get the service settings key.
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- WhiteListingFeatureElementIndex.COMPONENT_ID,
- WhiteListingFeatureElementIndex.LABEL_ID,
- WhiteListingFeatureElementIndex.ICON_ID,
- WhiteListingFeatureElementIndex.FRAGMENT_TYPE,
- WhiteListingFeatureElementIndex.SETTINGS_KEY,
- })
- public @interface WhiteListingFeatureElementIndex {
- int COMPONENT_ID = 0;
- int LABEL_ID = 1;
- int ICON_ID = 2;
- int FRAGMENT_TYPE = 3;
- int SETTINGS_KEY = 4;
- }
}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityActivityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityActivityTarget.java
new file mode 100644
index 0000000..4c7d93b
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityActivityTarget.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static com.android.internal.accessibility.util.ShortcutUtils.convertToKey;
+import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType;
+import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
+
+import android.accessibilityservice.AccessibilityShortcutInfo;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
+
+/**
+ * Base class for creating accessibility activity target.
+ */
+class AccessibilityActivityTarget extends AccessibilityTarget {
+
+ AccessibilityActivityTarget(Context context, @ShortcutType int shortcutType,
+ @NonNull AccessibilityShortcutInfo shortcutInfo) {
+ super(context,
+ shortcutType,
+ AccessibilityFragmentType.LAUNCH_ACTIVITY,
+ isShortcutContained(context, shortcutType,
+ shortcutInfo.getComponentName().flattenToString()),
+ shortcutInfo.getComponentName().flattenToString(),
+ shortcutInfo.getActivityInfo().loadLabel(context.getPackageManager()),
+ shortcutInfo.getActivityInfo().loadIcon(context.getPackageManager()),
+ convertToKey(convertToUserType(shortcutType)));
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java
new file mode 100644
index 0000000..e64f78a
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static com.android.internal.accessibility.util.ShortcutUtils.convertToKey;
+import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType;
+import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
+
+/**
+ * Base class for creating accessibility service target with various fragment types related to
+ * legacy type, invisible type and intuitive type.
+ */
+class AccessibilityServiceTarget extends AccessibilityTarget {
+
+ AccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
+ @AccessibilityFragmentType int fragmentType,
+ @NonNull AccessibilityServiceInfo serviceInfo) {
+ super(context,
+ shortcutType,
+ fragmentType,
+ isShortcutContained(context, shortcutType,
+ serviceInfo.getComponentName().flattenToString()),
+ serviceInfo.getComponentName().flattenToString(),
+ serviceInfo.getResolveInfo().loadLabel(context.getPackageManager()),
+ serviceInfo.getResolveInfo().loadIcon(context.getPackageManager()),
+ convertToKey(convertToUserType(shortcutType)));
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
index 9338c3c..e8d2813 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
@@ -19,64 +19,27 @@
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
import static android.view.accessibility.AccessibilityManager.ShortcutType;
-import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
-import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
-import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
-import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
import static com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
-import static com.android.internal.accessibility.common.ShortcutConstants.TargetType;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
-import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.COMPONENT_ID;
-import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.FRAGMENT_TYPE;
-import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.ICON_ID;
-import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.LABEL_ID;
-import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.SETTINGS_KEY;
-import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
-import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState;
-import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType;
-import static com.android.internal.accessibility.util.ShortcutUtils.hasValuesInSettings;
-import static com.android.internal.accessibility.util.ShortcutUtils.optInValueToSettings;
-import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings;
+import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.createEnableDialogContentView;
+import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getInstalledTargets;
+import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
import static com.android.internal.util.Preconditions.checkArgument;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.AccessibilityShortcutInfo;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
-import android.app.ActivityManager;
import android.app.AlertDialog;
-import android.content.ComponentName;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.Bundle;
-import android.os.storage.StorageManager;
-import android.provider.Settings;
-import android.text.BidiFormatter;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.Window;
import android.view.accessibility.AccessibilityManager;
import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.ImageView;
-import android.widget.Switch;
-import android.widget.TextView;
-import android.widget.Toast;
import com.android.internal.R;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-import java.util.Locale;
/**
* Activity used to display various targets related to accessibility service, accessibility
@@ -84,38 +47,11 @@
*/
public class AccessibilityShortcutChooserActivity extends Activity {
@ShortcutType
- private static int sShortcutType;
- @UserShortcutType
- private int mShortcutUserType;
- private final List<AccessibilityButtonTarget> mTargets = new ArrayList<>();
- private AlertDialog mAlertDialog;
- private AlertDialog mEnableDialog;
- private TargetAdapter mTargetAdapter;
- private AccessibilityButtonTarget mCurrentCheckedTarget;
-
- private static final String[][] WHITE_LISTING_FEATURES = {
- {
- COLOR_INVERSION_COMPONENT_NAME.flattenToString(),
- String.valueOf(R.string.color_inversion_feature_name),
- String.valueOf(R.drawable.ic_accessibility_color_inversion),
- String.valueOf(AccessibilityFragmentType.TOGGLE),
- Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
- },
- {
- DALTONIZER_COMPONENT_NAME.flattenToString(),
- String.valueOf(R.string.color_correction_feature_name),
- String.valueOf(R.drawable.ic_accessibility_color_correction),
- String.valueOf(AccessibilityFragmentType.TOGGLE),
- Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
- },
- {
- MAGNIFICATION_CONTROLLER_NAME,
- String.valueOf(R.string.accessibility_magnification_chooser_text),
- String.valueOf(R.drawable.ic_accessibility_magnification),
- String.valueOf(AccessibilityFragmentType.INVISIBLE_TOGGLE),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
- },
- };
+ private int mShortcutType;
+ private final List<AccessibilityTarget> mTargets = new ArrayList<>();
+ private AlertDialog mMenuDialog;
+ private AlertDialog mPermissionDialog;
+ private ShortcutTargetAdapter mTargetAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -126,20 +62,18 @@
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
- sShortcutType = getIntent().getIntExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE,
+ mShortcutType = getIntent().getIntExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE,
/* unexpectedShortcutType */ -1);
- final boolean existInShortcutType = (sShortcutType == ACCESSIBILITY_BUTTON)
- || (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY);
- checkArgument(existInShortcutType, "Unexpected shortcut type: " + sShortcutType);
+ final boolean existInShortcutType = (mShortcutType == ACCESSIBILITY_BUTTON)
+ || (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY);
+ checkArgument(existInShortcutType, "Unexpected shortcut type: " + mShortcutType);
- mShortcutUserType = convertToUserType(sShortcutType);
-
- mTargets.addAll(getServiceTargets(this, sShortcutType));
+ mTargets.addAll(getTargets(this, mShortcutType));
final String selectDialogTitle =
getString(R.string.accessibility_select_shortcut_menu_title);
- mTargetAdapter = new TargetAdapter(mTargets);
- mAlertDialog = new AlertDialog.Builder(this)
+ mTargetAdapter = new ShortcutTargetAdapter(mTargets);
+ mMenuDialog = new AlertDialog.Builder(this)
.setTitle(selectDialogTitle)
.setAdapter(mTargetAdapter, /* listener= */ null)
.setPositiveButton(
@@ -147,561 +81,55 @@
/* listener= */ null)
.setOnDismissListener(dialog -> finish())
.create();
- mAlertDialog.setOnShowListener(dialog -> updateDialogListeners());
- mAlertDialog.show();
+ mMenuDialog.setOnShowListener(dialog -> updateDialogListeners());
+ mMenuDialog.show();
}
@Override
protected void onDestroy() {
- mAlertDialog.dismiss();
+ mMenuDialog.dismiss();
super.onDestroy();
}
- private static List<AccessibilityButtonTarget> getServiceTargets(@NonNull Context context,
- @ShortcutType int shortcutType) {
- final List<AccessibilityButtonTarget> targets = getInstalledServiceTargets(context);
- final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
- final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType);
- targets.removeIf(target -> !requiredTargets.contains(target.getId()));
-
- return targets;
- }
-
- private static List<AccessibilityButtonTarget> getInstalledServiceTargets(
- @NonNull Context context) {
- final List<AccessibilityButtonTarget> targets = new ArrayList<>();
- targets.addAll(getAccessibilityFilteredTargets(context));
- targets.addAll(getWhiteListingServiceTargets(context));
-
- return targets;
- }
-
- private static List<AccessibilityButtonTarget> getAccessibilityFilteredTargets(
- @NonNull Context context) {
- final List<AccessibilityButtonTarget> serviceTargets =
- getAccessibilityServiceTargets(context);
- final List<AccessibilityButtonTarget> activityTargets =
- getAccessibilityActivityTargets(context);
-
- for (AccessibilityButtonTarget activityTarget : activityTargets) {
- serviceTargets.removeIf(serviceTarget -> {
- final ComponentName serviceComponentName =
- ComponentName.unflattenFromString(serviceTarget.getId());
- final ComponentName activityComponentName =
- ComponentName.unflattenFromString(activityTarget.getId());
- final boolean isSamePackageName = activityComponentName.getPackageName().equals(
- serviceComponentName.getPackageName());
- final boolean isSameLabel = activityTarget.getLabel().equals(
- serviceTarget.getLabel());
-
- return isSamePackageName && isSameLabel;
- });
- }
-
- final List<AccessibilityButtonTarget> targets = new ArrayList<>();
- targets.addAll(serviceTargets);
- targets.addAll(activityTargets);
-
- return targets;
- }
-
- private static List<AccessibilityButtonTarget> getAccessibilityServiceTargets(
- @NonNull Context context) {
- final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
- final List<AccessibilityServiceInfo> installedServices =
- ams.getInstalledAccessibilityServiceList();
- if (installedServices == null) {
- return Collections.emptyList();
- }
-
- final List<AccessibilityButtonTarget> targets = new ArrayList<>(installedServices.size());
- for (AccessibilityServiceInfo info : installedServices) {
- final int targetSdk =
- info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion;
- final boolean hasRequestAccessibilityButtonFlag =
- (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
- if ((targetSdk < Build.VERSION_CODES.R) && !hasRequestAccessibilityButtonFlag
- && (sShortcutType == ACCESSIBILITY_BUTTON)) {
- continue;
- }
- targets.add(new AccessibilityButtonTarget(context, info));
- }
-
- return targets;
- }
-
- private static List<AccessibilityButtonTarget> getAccessibilityActivityTargets(
- @NonNull Context context) {
- final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
- final List<AccessibilityShortcutInfo> installedServices =
- ams.getInstalledAccessibilityShortcutListAsUser(context,
- ActivityManager.getCurrentUser());
- if (installedServices == null) {
- return Collections.emptyList();
- }
-
- final List<AccessibilityButtonTarget> targets = new ArrayList<>(installedServices.size());
- for (AccessibilityShortcutInfo info : installedServices) {
- targets.add(new AccessibilityButtonTarget(context, info));
- }
-
- return targets;
- }
-
- private static List<AccessibilityButtonTarget> getWhiteListingServiceTargets(
- @NonNull Context context) {
- final List<AccessibilityButtonTarget> targets = new ArrayList<>();
-
- for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) {
- final AccessibilityButtonTarget target = new AccessibilityButtonTarget(
- context,
- WHITE_LISTING_FEATURES[i][COMPONENT_ID],
- Integer.parseInt(WHITE_LISTING_FEATURES[i][LABEL_ID]),
- Integer.parseInt(WHITE_LISTING_FEATURES[i][ICON_ID]),
- Integer.parseInt(WHITE_LISTING_FEATURES[i][FRAGMENT_TYPE]));
- targets.add(target);
- }
-
- return targets;
- }
-
- private static boolean isWhiteListingServiceEnabled(@NonNull Context context,
- AccessibilityButtonTarget target) {
-
- for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) {
- if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(target.getId())) {
- return Settings.Secure.getInt(context.getContentResolver(),
- WHITE_LISTING_FEATURES[i][SETTINGS_KEY],
- /* settingsValueOff */ 0) == /* settingsValueOn */ 1;
- }
- }
-
- return false;
- }
-
- private static boolean isWhiteListingService(String componentId) {
- for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) {
- if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(componentId)) {
- return true;
- }
- }
-
- return false;
- }
-
- private void setWhiteListingServiceEnabled(String componentId, int settingsValue) {
- for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) {
- if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(componentId)) {
- Settings.Secure.putInt(getContentResolver(),
- WHITE_LISTING_FEATURES[i][SETTINGS_KEY], settingsValue);
- return;
- }
- }
- }
-
- private void setServiceEnabled(String componentId, boolean enabled) {
- if (isWhiteListingService(componentId)) {
- setWhiteListingServiceEnabled(componentId,
- enabled ? /* settingsValueOn */ 1 : /* settingsValueOff */ 0);
- } else {
- final ComponentName componentName = ComponentName.unflattenFromString(componentId);
- setAccessibilityServiceState(this, componentName, enabled);
- }
- }
-
- private static class ViewHolder {
- View mItemView;
- CheckBox mCheckBox;
- ImageView mIconView;
- TextView mLabelView;
- Switch mSwitchItem;
- }
-
- private static class TargetAdapter extends BaseAdapter {
- @ShortcutMenuMode
- private int mShortcutMenuMode = ShortcutMenuMode.LAUNCH;
- private List<AccessibilityButtonTarget> mButtonTargets;
-
- TargetAdapter(List<AccessibilityButtonTarget> targets) {
- this.mButtonTargets = targets;
- }
-
- void setShortcutMenuMode(@ShortcutMenuMode int shortcutMenuMode) {
- mShortcutMenuMode = shortcutMenuMode;
- }
-
- @ShortcutMenuMode
- int getShortcutMenuMode() {
- return mShortcutMenuMode;
- }
-
- @Override
- public int getCount() {
- return mButtonTargets.size();
- }
-
- @Override
- public Object getItem(int position) {
- return mButtonTargets.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final Context context = parent.getContext();
- ViewHolder holder;
- if (convertView == null) {
- convertView = LayoutInflater.from(context).inflate(
- R.layout.accessibility_shortcut_chooser_item, parent, /* attachToRoot= */
- false);
- holder = new ViewHolder();
- holder.mItemView = convertView;
- holder.mCheckBox = convertView.findViewById(
- R.id.accessibility_shortcut_target_checkbox);
- holder.mIconView = convertView.findViewById(
- R.id.accessibility_shortcut_target_icon);
- holder.mLabelView = convertView.findViewById(
- R.id.accessibility_shortcut_target_label);
- holder.mSwitchItem = convertView.findViewById(
- R.id.accessibility_shortcut_target_switch_item);
- convertView.setTag(holder);
- } else {
- holder = (ViewHolder) convertView.getTag();
- }
-
- final AccessibilityButtonTarget target = mButtonTargets.get(position);
- updateActionItem(context, holder, target);
-
- return convertView;
- }
-
- private void updateActionItem(@NonNull Context context,
- @NonNull ViewHolder holder, AccessibilityButtonTarget target) {
-
- switch (target.getFragmentType()) {
- case AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE:
- updateVolumeShortcutToggleTargetActionItemVisibility(holder, target);
- break;
- case AccessibilityFragmentType.INVISIBLE_TOGGLE:
- updateInvisibleToggleTargetActionItemVisibility(holder, target);
- break;
- case AccessibilityFragmentType.TOGGLE:
- updateToggleTargetActionItemVisibility(context, holder, target);
- break;
- case AccessibilityFragmentType.LAUNCH_ACTIVITY:
- updateLaunchActivityTargetActionItemVisibility(holder, target);
- break;
- default:
- throw new IllegalStateException("Unexpected fragment type");
- }
- }
-
- private void updateVolumeShortcutToggleTargetActionItemVisibility(
- @NonNull ViewHolder holder, AccessibilityButtonTarget target) {
- final boolean isLaunchMenuMode = (mShortcutMenuMode == ShortcutMenuMode.LAUNCH);
-
- holder.mCheckBox.setChecked(!isLaunchMenuMode && target.isChecked());
- holder.mCheckBox.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE);
- holder.mIconView.setImageDrawable(target.getDrawable());
- holder.mLabelView.setText(target.getLabel());
- holder.mSwitchItem.setVisibility(View.GONE);
- }
-
- private void updateInvisibleToggleTargetActionItemVisibility(@NonNull ViewHolder holder,
- AccessibilityButtonTarget target) {
- final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
-
- holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked());
- holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
- holder.mIconView.setImageDrawable(target.getDrawable());
- holder.mLabelView.setText(target.getLabel());
- holder.mSwitchItem.setVisibility(View.GONE);
- }
-
- private void updateToggleTargetActionItemVisibility(@NonNull Context context,
- @NonNull ViewHolder holder, AccessibilityButtonTarget target) {
- final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
- final boolean isServiceEnabled = isWhiteListingService(target.getId())
- ? isWhiteListingServiceEnabled(context, target)
- : isAccessibilityServiceEnabled(context, target);
-
- holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked());
- holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
- holder.mIconView.setImageDrawable(target.getDrawable());
- holder.mLabelView.setText(target.getLabel());
- holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
- holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled);
- }
-
- private void updateLaunchActivityTargetActionItemVisibility(@NonNull ViewHolder holder,
- AccessibilityButtonTarget target) {
- final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
-
- holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked());
- holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
- holder.mIconView.setImageDrawable(target.getDrawable());
- holder.mLabelView.setText(target.getLabel());
- holder.mSwitchItem.setVisibility(View.GONE);
- }
- }
-
- private static class AccessibilityButtonTarget {
- private String mId;
- @TargetType
- private int mType;
- private boolean mChecked;
- private CharSequence mLabel;
- private Drawable mDrawable;
- @AccessibilityFragmentType
- private int mFragmentType;
-
- AccessibilityButtonTarget(@NonNull Context context,
- @NonNull AccessibilityServiceInfo serviceInfo) {
- this.mId = serviceInfo.getComponentName().flattenToString();
- this.mType = TargetType.ACCESSIBILITY_SERVICE;
- this.mChecked = isTargetShortcutUsed(context, mId);
- this.mLabel = serviceInfo.getResolveInfo().loadLabel(context.getPackageManager());
- this.mDrawable = serviceInfo.getResolveInfo().loadIcon(context.getPackageManager());
- this.mFragmentType = getAccessibilityServiceFragmentType(serviceInfo);
- }
-
- AccessibilityButtonTarget(@NonNull Context context,
- @NonNull AccessibilityShortcutInfo shortcutInfo) {
- this.mId = shortcutInfo.getComponentName().flattenToString();
- this.mType = TargetType.ACCESSIBILITY_ACTIVITY;
- this.mChecked = isTargetShortcutUsed(context, mId);
- this.mLabel = shortcutInfo.getActivityInfo().loadLabel(context.getPackageManager());
- this.mDrawable = shortcutInfo.getActivityInfo().loadIcon(context.getPackageManager());
- this.mFragmentType = AccessibilityFragmentType.LAUNCH_ACTIVITY;
- }
-
- AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId,
- int iconRes, @AccessibilityFragmentType int fragmentType) {
- this.mId = id;
- this.mType = TargetType.WHITE_LISTING;
- this.mChecked = isTargetShortcutUsed(context, mId);
- this.mLabel = context.getText(labelResId);
- this.mDrawable = context.getDrawable(iconRes);
- this.mFragmentType = fragmentType;
- }
-
- public void setChecked(boolean checked) {
- mChecked = checked;
- }
-
- public String getId() {
- return mId;
- }
-
- public int getType() {
- return mType;
- }
-
- public boolean isChecked() {
- return mChecked;
- }
-
- public CharSequence getLabel() {
- return mLabel;
- }
-
- public Drawable getDrawable() {
- return mDrawable;
- }
-
- public int getFragmentType() {
- return mFragmentType;
- }
- }
-
- private static boolean isAccessibilityServiceEnabled(@NonNull Context context,
- AccessibilityButtonTarget target) {
- final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
- final List<AccessibilityServiceInfo> enabledServices =
- ams.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
-
- for (AccessibilityServiceInfo info : enabledServices) {
- final String id = info.getComponentName().flattenToString();
- if (id.equals(target.getId())) {
- return true;
- }
- }
-
- return false;
- }
-
private void onTargetSelected(AdapterView<?> parent, View view, int position, long id) {
- final AccessibilityButtonTarget target = mTargets.get(position);
- switch (target.getFragmentType()) {
- case AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE:
- onVolumeShortcutToggleTargetSelected(target);
- break;
- case AccessibilityFragmentType.INVISIBLE_TOGGLE:
- onInvisibleToggleTargetSelected(target);
- break;
- case AccessibilityFragmentType.TOGGLE:
- onToggleTargetSelected(target);
- break;
- case AccessibilityFragmentType.LAUNCH_ACTIVITY:
- onLaunchActivityTargetSelected(target);
- break;
- default:
- throw new IllegalStateException("Unexpected fragment type");
- }
-
- mAlertDialog.dismiss();
- }
-
- private void onVolumeShortcutToggleTargetSelected(AccessibilityButtonTarget target) {
- if (sShortcutType == ACCESSIBILITY_BUTTON) {
- final AccessibilityManager ams = getSystemService(AccessibilityManager.class);
- ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
- } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
- switchServiceState(target);
- }
- }
-
- private void onInvisibleToggleTargetSelected(AccessibilityButtonTarget target) {
- final AccessibilityManager ams = getSystemService(AccessibilityManager.class);
- if (sShortcutType == ACCESSIBILITY_BUTTON) {
- ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
- } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
- ams.performAccessibilityShortcut(target.getId());
- }
- }
-
- private void onToggleTargetSelected(AccessibilityButtonTarget target) {
- switchServiceState(target);
- }
-
- private void onLaunchActivityTargetSelected(AccessibilityButtonTarget target) {
- final AccessibilityManager ams = getSystemService(AccessibilityManager.class);
- if (sShortcutType == ACCESSIBILITY_BUTTON) {
- ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
- } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
- ams.performAccessibilityShortcut(target.getId());
- }
- }
-
- private void switchServiceState(AccessibilityButtonTarget target) {
- final ComponentName componentName =
- ComponentName.unflattenFromString(target.getId());
- final String componentId = componentName.flattenToString();
-
- if (isWhiteListingService(componentId)) {
- setWhiteListingServiceEnabled(componentId,
- isWhiteListingServiceEnabled(this, target)
- ? /* settingsValueOff */ 0
- : /* settingsValueOn */ 1);
- } else {
- setAccessibilityServiceState(this, componentName,
- /* enabled= */!isAccessibilityServiceEnabled(this, target));
- }
+ final AccessibilityTarget target = mTargets.get(position);
+ target.onSelected();
+ mMenuDialog.dismiss();
}
private void onTargetChecked(AdapterView<?> parent, View view, int position, long id) {
- mCurrentCheckedTarget = mTargets.get(position);
+ final AccessibilityTarget target = mTargets.get(position);
- if ((mCurrentCheckedTarget.getType() == TargetType.ACCESSIBILITY_SERVICE)
- && !mCurrentCheckedTarget.isChecked()) {
- mEnableDialog = new AlertDialog.Builder(this)
- .setView(createEnableDialogContentView(this, mCurrentCheckedTarget,
- this::onPermissionAllowButtonClicked,
- this::onPermissionDenyButtonClicked))
+ if ((target instanceof AccessibilityServiceTarget) && !target.isShortcutEnabled()) {
+ mPermissionDialog = new AlertDialog.Builder(this)
+ .setView(createEnableDialogContentView(this,
+ (AccessibilityServiceTarget) target,
+ v -> {
+ mPermissionDialog.dismiss();
+ mTargetAdapter.notifyDataSetChanged();
+ },
+ v -> mPermissionDialog.dismiss()))
.create();
- mEnableDialog.show();
+ mPermissionDialog.show();
return;
}
- onTargetChecked(mCurrentCheckedTarget, !mCurrentCheckedTarget.isChecked());
- }
-
- private void onTargetChecked(AccessibilityButtonTarget target, boolean checked) {
- switch (target.getFragmentType()) {
- case AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE:
- onVolumeShortcutToggleTargetChecked(checked);
- break;
- case AccessibilityFragmentType.INVISIBLE_TOGGLE:
- onInvisibleToggleTargetChecked(checked);
- break;
- case AccessibilityFragmentType.TOGGLE:
- onToggleTargetChecked(checked);
- break;
- case AccessibilityFragmentType.LAUNCH_ACTIVITY:
- onLaunchActivityTargetChecked(checked);
- break;
- default:
- throw new IllegalStateException("Unexpected fragment type");
- }
- }
-
- private void onVolumeShortcutToggleTargetChecked(boolean checked) {
- if (sShortcutType == ACCESSIBILITY_BUTTON) {
- setServiceEnabled(mCurrentCheckedTarget.getId(), checked);
- if (!checked) {
- optOutValueFromSettings(this, HARDWARE, mCurrentCheckedTarget.getId());
- final String warningText =
- getString(R.string.accessibility_uncheck_legacy_item_warning,
- mCurrentCheckedTarget.getLabel());
- Toast.makeText(this, warningText, Toast.LENGTH_SHORT).show();
- }
- } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
- updateValueToSettings(mCurrentCheckedTarget.getId(), checked);
- } else {
- throw new IllegalStateException("Unexpected shortcut type");
- }
-
- mCurrentCheckedTarget.setChecked(checked);
+ target.onCheckedChanged(!target.isShortcutEnabled());
mTargetAdapter.notifyDataSetChanged();
}
- private void onInvisibleToggleTargetChecked(boolean checked) {
- final int shortcutTypes = UserShortcutType.SOFTWARE | HARDWARE;
- if (!hasValuesInSettings(this, shortcutTypes, mCurrentCheckedTarget.getId())) {
- setServiceEnabled(mCurrentCheckedTarget.getId(), checked);
- }
-
- updateValueToSettings(mCurrentCheckedTarget.getId(), checked);
- mCurrentCheckedTarget.setChecked(checked);
- mTargetAdapter.notifyDataSetChanged();
- }
-
- private void onToggleTargetChecked(boolean checked) {
- updateValueToSettings(mCurrentCheckedTarget.getId(), checked);
- mCurrentCheckedTarget.setChecked(checked);
- mTargetAdapter.notifyDataSetChanged();
- }
-
- private void onLaunchActivityTargetChecked(boolean checked) {
- updateValueToSettings(mCurrentCheckedTarget.getId(), checked);
- mCurrentCheckedTarget.setChecked(checked);
- mTargetAdapter.notifyDataSetChanged();
- }
-
- private void updateValueToSettings(String componentId, boolean checked) {
- if (checked) {
- optInValueToSettings(this, mShortcutUserType, componentId);
- } else {
- optOutValueFromSettings(this, mShortcutUserType, componentId);
- }
- }
-
private void onDoneButtonClicked() {
mTargets.clear();
- mTargets.addAll(getServiceTargets(this, sShortcutType));
+ mTargets.addAll(getTargets(this, mShortcutType));
if (mTargets.isEmpty()) {
- mAlertDialog.dismiss();
+ mMenuDialog.dismiss();
return;
}
mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
mTargetAdapter.notifyDataSetChanged();
- mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+ mMenuDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
getString(R.string.edit_accessibility_shortcut_menu_button));
updateDialogListeners();
@@ -709,11 +137,11 @@
private void onEditButtonClicked() {
mTargets.clear();
- mTargets.addAll(getInstalledServiceTargets(this));
+ mTargets.addAll(getInstalledTargets(this, mShortcutType));
mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.EDIT);
mTargetAdapter.notifyDataSetChanged();
- mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+ mMenuDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
getString(R.string.done_accessibility_shortcut_menu_button));
updateDialogListeners();
@@ -724,79 +152,14 @@
(mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT);
final int selectDialogTitleId = R.string.accessibility_select_shortcut_menu_title;
final int editDialogTitleId =
- (sShortcutType == ACCESSIBILITY_BUTTON)
+ (mShortcutType == ACCESSIBILITY_BUTTON)
? R.string.accessibility_edit_shortcut_menu_button_title
: R.string.accessibility_edit_shortcut_menu_volume_title;
- mAlertDialog.setTitle(getString(isEditMenuMode ? editDialogTitleId : selectDialogTitleId));
- mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(
+ mMenuDialog.setTitle(getString(isEditMenuMode ? editDialogTitleId : selectDialogTitleId));
+ mMenuDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(
isEditMenuMode ? view -> onDoneButtonClicked() : view -> onEditButtonClicked());
- mAlertDialog.getListView().setOnItemClickListener(
+ mMenuDialog.getListView().setOnItemClickListener(
isEditMenuMode ? this::onTargetChecked : this::onTargetSelected);
}
-
- private static boolean isTargetShortcutUsed(@NonNull Context context, String id) {
- final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
- final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(sShortcutType);
- return requiredTargets.contains(id);
- }
-
- private void onPermissionAllowButtonClicked(View view) {
- if (mCurrentCheckedTarget.getFragmentType()
- != AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE) {
- updateValueToSettings(mCurrentCheckedTarget.getId(), /* checked= */ true);
- }
- onTargetChecked(mCurrentCheckedTarget, /* checked= */ true);
- mEnableDialog.dismiss();
- }
-
- private void onPermissionDenyButtonClicked(View view) {
- mEnableDialog.dismiss();
- }
-
- private static View createEnableDialogContentView(Context context,
- AccessibilityButtonTarget target, View.OnClickListener allowListener,
- View.OnClickListener denyListener) {
- final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- final View content = inflater.inflate(
- R.layout.accessibility_enable_service_encryption_warning, /* root= */ null);
-
- final TextView encryptionWarningView = (TextView) content.findViewById(
- R.id.accessibility_encryption_warning);
- if (StorageManager.isNonDefaultBlockEncrypted()) {
- final String text = context.getString(
- R.string.accessibility_enable_service_encryption_warning,
- getServiceName(context, target.getLabel()));
- encryptionWarningView.setText(text);
- encryptionWarningView.setVisibility(View.VISIBLE);
- } else {
- encryptionWarningView.setVisibility(View.GONE);
- }
-
- final ImageView permissionDialogIcon = content.findViewById(
- R.id.accessibility_permissionDialog_icon);
- permissionDialogIcon.setImageDrawable(target.getDrawable());
-
- final TextView permissionDialogTitle = content.findViewById(
- R.id.accessibility_permissionDialog_title);
- permissionDialogTitle.setText(context.getString(R.string.accessibility_enable_service_title,
- getServiceName(context, target.getLabel())));
-
- final Button permissionAllowButton = content.findViewById(
- R.id.accessibility_permission_enable_allow_button);
- final Button permissionDenyButton = content.findViewById(
- R.id.accessibility_permission_enable_deny_button);
- permissionAllowButton.setOnClickListener(allowListener);
- permissionDenyButton.setOnClickListener(denyListener);
-
- return content;
- }
-
- // Gets the service name and bidi wrap it to protect from bidi side effects.
- private static CharSequence getServiceName(Context context, CharSequence label) {
- final Locale locale = context.getResources().getConfiguration().getLocales().get(0);
- return BidiFormatter.getInstance(locale).unicodeWrap(label);
- }
}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
new file mode 100644
index 0000000..72ebc58
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
+
+import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType;
+import static com.android.internal.accessibility.util.ShortcutUtils.optInValueToSettings;
+import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
+import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
+
+/**
+ * Abstract base class for creating various target related to accessibility service,
+ * accessibility activity, and white listing feature.
+ */
+abstract class AccessibilityTarget implements TargetOperations, OnTargetSelectedListener,
+ OnTargetCheckedChangeListener {
+ private Context mContext;
+ @ShortcutType
+ private int mShortcutType;
+ @AccessibilityFragmentType
+ private int mFragmentType;
+ private boolean mShortcutEnabled;
+ private String mId;
+ private CharSequence mLabel;
+ private Drawable mIcon;
+ private String mKey;
+
+ AccessibilityTarget(Context context, @ShortcutType int shortcutType,
+ @AccessibilityFragmentType int fragmentType, boolean isShortcutSwitched, String id,
+ CharSequence label, Drawable icon, String key) {
+ mContext = context;
+ mShortcutType = shortcutType;
+ mFragmentType = fragmentType;
+ mShortcutEnabled = isShortcutSwitched;
+ mId = id;
+ mLabel = label;
+ mIcon = icon;
+ mKey = key;
+ }
+
+ @Override
+ public void updateActionItem(@NonNull ViewHolder holder,
+ @ShortcutConstants.ShortcutMenuMode int shortcutMenuMode) {
+ final boolean isEditMenuMode =
+ shortcutMenuMode == ShortcutConstants.ShortcutMenuMode.EDIT;
+
+ holder.mCheckBoxView.setChecked(isEditMenuMode && isShortcutEnabled());
+ holder.mCheckBoxView.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+ holder.mIconView.setImageDrawable(getIcon());
+ holder.mLabelView.setText(getLabel());
+ holder.mSwitchItem.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onSelected() {
+ final AccessibilityManager am =
+ getContext().getSystemService(AccessibilityManager.class);
+ switch (getShortcutType()) {
+ case ACCESSIBILITY_BUTTON:
+ am.notifyAccessibilityButtonClicked(getContext().getDisplayId(), getId());
+ return;
+ case ACCESSIBILITY_SHORTCUT_KEY:
+ am.performAccessibilityShortcut(getId());
+ return;
+ default:
+ throw new IllegalStateException("Unexpected shortcut type");
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(boolean isChecked) {
+ setShortcutEnabled(isChecked);
+ if (isChecked) {
+ optInValueToSettings(getContext(), convertToUserType(getShortcutType()), getId());
+ } else {
+ optOutValueFromSettings(getContext(), convertToUserType(getShortcutType()), getId());
+ }
+ }
+
+ public void setShortcutEnabled(boolean enabled) {
+ mShortcutEnabled = enabled;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public @ShortcutType int getShortcutType() {
+ return mShortcutType;
+ }
+
+ public @AccessibilityFragmentType int getFragmentType() {
+ return mFragmentType;
+ }
+
+ public boolean isShortcutEnabled() {
+ return mShortcutEnabled;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ public String getKey() {
+ return mKey;
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
new file mode 100644
index 0000000..f63cbe0
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
+import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityShortcutInfo;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Build;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.text.BidiFormatter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.ShortcutType;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Collection of utilities for accessibility target.
+ */
+final class AccessibilityTargetHelper {
+ private AccessibilityTargetHelper() {}
+
+ static List<AccessibilityTarget> getTargets(Context context,
+ @ShortcutType int shortcutType) {
+ final List<AccessibilityTarget> targets = getInstalledTargets(context, shortcutType);
+ final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
+ final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType);
+ targets.removeIf(target -> !requiredTargets.contains(target.getId()));
+
+ return targets;
+ }
+
+ static List<AccessibilityTarget> getInstalledTargets(Context context,
+ @ShortcutType int shortcutType) {
+ final List<AccessibilityTarget> targets = new ArrayList<>();
+ targets.addAll(getAccessibilityFilteredTargets(context, shortcutType));
+ targets.addAll(getWhiteListingFeatureTargets(context, shortcutType));
+
+ return targets;
+ }
+
+ private static List<AccessibilityTarget> getAccessibilityFilteredTargets(Context context,
+ @ShortcutType int shortcutType) {
+ final List<AccessibilityTarget> serviceTargets =
+ getAccessibilityServiceTargets(context, shortcutType);
+ final List<AccessibilityTarget> activityTargets =
+ getAccessibilityActivityTargets(context, shortcutType);
+
+ for (AccessibilityTarget activityTarget : activityTargets) {
+ serviceTargets.removeIf(
+ serviceTarget -> arePackageNameAndLabelTheSame(serviceTarget, activityTarget));
+ }
+
+ final List<AccessibilityTarget> targets = new ArrayList<>();
+ targets.addAll(serviceTargets);
+ targets.addAll(activityTargets);
+
+ return targets;
+ }
+
+ private static boolean arePackageNameAndLabelTheSame(@NonNull AccessibilityTarget serviceTarget,
+ @NonNull AccessibilityTarget activityTarget) {
+ final ComponentName serviceComponentName =
+ ComponentName.unflattenFromString(serviceTarget.getId());
+ final ComponentName activityComponentName =
+ ComponentName.unflattenFromString(activityTarget.getId());
+ final boolean isSamePackageName = activityComponentName.getPackageName().equals(
+ serviceComponentName.getPackageName());
+ final boolean isSameLabel = activityTarget.getLabel().equals(
+ serviceTarget.getLabel());
+
+ return isSamePackageName && isSameLabel;
+ }
+
+ private static List<AccessibilityTarget> getAccessibilityServiceTargets(Context context,
+ @ShortcutType int shortcutType) {
+ final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
+ final List<AccessibilityServiceInfo> installedServices =
+ ams.getInstalledAccessibilityServiceList();
+ if (installedServices == null) {
+ return Collections.emptyList();
+ }
+
+ final List<AccessibilityTarget> targets = new ArrayList<>(installedServices.size());
+ for (AccessibilityServiceInfo info : installedServices) {
+ final int targetSdk =
+ info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion;
+ final boolean hasRequestAccessibilityButtonFlag =
+ (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ if ((targetSdk <= Build.VERSION_CODES.Q) && !hasRequestAccessibilityButtonFlag
+ && (shortcutType == ACCESSIBILITY_BUTTON)) {
+ continue;
+ }
+
+ targets.add(createAccessibilityServiceTarget(context, shortcutType, info));
+ }
+
+ return targets;
+ }
+
+ private static List<AccessibilityTarget> getAccessibilityActivityTargets(Context context,
+ @ShortcutType int shortcutType) {
+ final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
+ final List<AccessibilityShortcutInfo> installedServices =
+ ams.getInstalledAccessibilityShortcutListAsUser(context,
+ ActivityManager.getCurrentUser());
+ if (installedServices == null) {
+ return Collections.emptyList();
+ }
+
+ final List<AccessibilityTarget> targets = new ArrayList<>(installedServices.size());
+ for (AccessibilityShortcutInfo info : installedServices) {
+ targets.add(new AccessibilityActivityTarget(context, shortcutType, info));
+ }
+
+ return targets;
+ }
+
+ private static List<AccessibilityTarget> getWhiteListingFeatureTargets(Context context,
+ @ShortcutType int shortcutType) {
+ final List<AccessibilityTarget> targets = new ArrayList<>();
+
+ final InvisibleToggleWhiteListingFeatureTarget magnification =
+ new InvisibleToggleWhiteListingFeatureTarget(context,
+ shortcutType,
+ isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
+ MAGNIFICATION_CONTROLLER_NAME,
+ context.getString(R.string.accessibility_magnification_chooser_text),
+ context.getDrawable(R.drawable.ic_accessibility_magnification),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+
+ final ToggleWhiteListingFeatureTarget daltonizer =
+ new ToggleWhiteListingFeatureTarget(context,
+ shortcutType,
+ isShortcutContained(context, shortcutType,
+ DALTONIZER_COMPONENT_NAME.flattenToString()),
+ DALTONIZER_COMPONENT_NAME.flattenToString(),
+ context.getString(R.string.color_correction_feature_name),
+ context.getDrawable(R.drawable.ic_accessibility_color_correction),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
+
+ final ToggleWhiteListingFeatureTarget colorInversion =
+ new ToggleWhiteListingFeatureTarget(context,
+ shortcutType,
+ isShortcutContained(context, shortcutType,
+ COLOR_INVERSION_COMPONENT_NAME.flattenToString()),
+ COLOR_INVERSION_COMPONENT_NAME.flattenToString(),
+ context.getString(R.string.color_inversion_feature_name),
+ context.getDrawable(R.drawable.ic_accessibility_color_inversion),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+
+ targets.add(magnification);
+ targets.add(daltonizer);
+ targets.add(colorInversion);
+
+ return targets;
+ }
+
+ private static AccessibilityTarget createAccessibilityServiceTarget(Context context,
+ @ShortcutType int shortcutType, @NonNull AccessibilityServiceInfo info) {
+ switch (getAccessibilityServiceFragmentType(info)) {
+ case AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE:
+ return new VolumeShortcutToggleAccessibilityServiceTarget(context, shortcutType,
+ info);
+ case AccessibilityFragmentType.INVISIBLE_TOGGLE:
+ return new InvisibleToggleAccessibilityServiceTarget(context, shortcutType, info);
+ case AccessibilityFragmentType.TOGGLE:
+ return new ToggleAccessibilityServiceTarget(context, shortcutType, info);
+ default:
+ throw new IllegalStateException("Unexpected fragment type");
+ }
+ }
+
+ static View createEnableDialogContentView(Context context,
+ AccessibilityServiceTarget target, View.OnClickListener allowListener,
+ View.OnClickListener denyListener) {
+ final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ final View content = inflater.inflate(
+ R.layout.accessibility_enable_service_encryption_warning, /* root= */ null);
+
+ final TextView encryptionWarningView = (TextView) content.findViewById(
+ R.id.accessibility_encryption_warning);
+ if (StorageManager.isNonDefaultBlockEncrypted()) {
+ final String text = context.getString(
+ R.string.accessibility_enable_service_encryption_warning,
+ getServiceName(context, target.getLabel()));
+ encryptionWarningView.setText(text);
+ encryptionWarningView.setVisibility(View.VISIBLE);
+ } else {
+ encryptionWarningView.setVisibility(View.GONE);
+ }
+
+ final ImageView dialogIcon = content.findViewById(
+ R.id.accessibility_permissionDialog_icon);
+ dialogIcon.setImageDrawable(target.getIcon());
+
+ final TextView dialogTitle = content.findViewById(
+ R.id.accessibility_permissionDialog_title);
+ dialogTitle.setText(context.getString(R.string.accessibility_enable_service_title,
+ getServiceName(context, target.getLabel())));
+
+ final Button allowButton = content.findViewById(
+ R.id.accessibility_permission_enable_allow_button);
+ final Button denyButton = content.findViewById(
+ R.id.accessibility_permission_enable_deny_button);
+ allowButton.setOnClickListener((view) -> {
+ target.onCheckedChanged(/* isChecked= */ true);
+ allowListener.onClick(view);
+ });
+ denyButton.setOnClickListener((view) -> {
+ target.onCheckedChanged(/* isChecked= */ false);
+ denyListener.onClick(view);
+ });
+
+ return content;
+ }
+
+ // Gets the service name and bidi wrap it to protect from bidi side effects.
+ private static CharSequence getServiceName(Context context, CharSequence label) {
+ final Locale locale = context.getResources().getConfiguration().getLocales().get(0);
+ return BidiFormatter.getInstance(locale).unicodeWrap(label);
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java
new file mode 100644
index 0000000..9d5c374
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
+
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState;
+import static com.android.internal.accessibility.util.ShortcutUtils.isComponentIdExistingInSettings;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
+
+/**
+ * Extension for {@link AccessibilityServiceTarget} with
+ * {@link AccessibilityFragmentType#INVISIBLE_TOGGLE} type.
+ */
+class InvisibleToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
+
+ InvisibleToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
+ @NonNull AccessibilityServiceInfo serviceInfo) {
+ super(context,
+ shortcutType,
+ AccessibilityFragmentType.INVISIBLE_TOGGLE,
+ serviceInfo);
+ }
+
+ @Override
+ public void onCheckedChanged(boolean isChecked) {
+ final ComponentName componentName = ComponentName.unflattenFromString(getId());
+
+ if (!isComponentIdExistingInOtherShortcut()) {
+ setAccessibilityServiceState(getContext(), componentName, isChecked);
+ }
+
+ super.onCheckedChanged(isChecked);
+ }
+
+ private boolean isComponentIdExistingInOtherShortcut() {
+ switch (getShortcutType()) {
+ case ACCESSIBILITY_BUTTON:
+ return isComponentIdExistingInSettings(getContext(), UserShortcutType.HARDWARE,
+ getId());
+ case ACCESSIBILITY_SHORTCUT_KEY:
+ return isComponentIdExistingInSettings(getContext(), UserShortcutType.SOFTWARE,
+ getId());
+ default:
+ throw new IllegalStateException("Unexpected shortcut type");
+ }
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java
new file mode 100644
index 0000000..acd101b
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
+
+/**
+ * Extension for {@link AccessibilityTarget} with {@link AccessibilityFragmentType#INVISIBLE_TOGGLE}
+ * type.
+ */
+class InvisibleToggleWhiteListingFeatureTarget extends AccessibilityTarget {
+
+ InvisibleToggleWhiteListingFeatureTarget(Context context, @ShortcutType int shortcutType,
+ boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) {
+ super(context, shortcutType, AccessibilityFragmentType.INVISIBLE_TOGGLE,
+ isShortcutSwitched, id, label, icon, key);
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/OnTargetCheckedChangeListener.java b/core/java/com/android/internal/accessibility/dialog/OnTargetCheckedChangeListener.java
new file mode 100644
index 0000000..dab45e4
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/OnTargetCheckedChangeListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+/**
+ * Interface definition for a callback to be invoked when the checked state
+ * of a accessibility target changed.
+ */
+interface OnTargetCheckedChangeListener {
+ /**
+ * Called when the checked state of a accessibility target has changed.
+ *
+ * @param isChecked The new checked state of accessibility target.
+ */
+ void onCheckedChanged(boolean isChecked);
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/OnTargetSelectedListener.java b/core/java/com/android/internal/accessibility/dialog/OnTargetSelectedListener.java
new file mode 100644
index 0000000..b3e976f2
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/OnTargetSelectedListener.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+/**
+ * Interface definition for a callback to be invoked when a accessibility target is selected.
+ */
+interface OnTargetSelectedListener {
+ /**
+ * Called when a accessibility target has been selected.
+ */
+ void onSelected();
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/ShortcutTargetAdapter.java b/core/java/com/android/internal/accessibility/dialog/ShortcutTargetAdapter.java
new file mode 100644
index 0000000..b7605b7
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/ShortcutTargetAdapter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.R;
+import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
+
+import java.util.List;
+
+/**
+ * Extension for {@link TargetAdapter} and used for AccessibilityShortcutChooserActivity.
+ */
+class ShortcutTargetAdapter extends TargetAdapter {
+ @ShortcutMenuMode
+ private int mShortcutMenuMode = ShortcutMenuMode.LAUNCH;
+ private final List<AccessibilityTarget> mTargets;
+
+ ShortcutTargetAdapter(@NonNull List<AccessibilityTarget> targets) {
+ mTargets = targets;
+ }
+
+ @Override
+ public int getCount() {
+ return mTargets.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mTargets.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final Context context = parent.getContext();
+ ViewHolder holder;
+ if (convertView == null) {
+ convertView = LayoutInflater.from(context).inflate(
+ R.layout.accessibility_shortcut_chooser_item, parent, /* attachToRoot= */
+ false);
+ holder = new ViewHolder();
+ holder.mCheckBoxView = convertView.findViewById(
+ R.id.accessibility_shortcut_target_checkbox);
+ holder.mIconView = convertView.findViewById(R.id.accessibility_shortcut_target_icon);
+ holder.mLabelView = convertView.findViewById(
+ R.id.accessibility_shortcut_target_label);
+ holder.mSwitchItem = convertView.findViewById(
+ R.id.accessibility_shortcut_target_switch_item);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ final AccessibilityTarget target = mTargets.get(position);
+ target.updateActionItem(holder, mShortcutMenuMode);
+
+ return convertView;
+ }
+
+ void setShortcutMenuMode(@ShortcutMenuMode int shortcutMenuMode) {
+ mShortcutMenuMode = shortcutMenuMode;
+ }
+
+ @ShortcutMenuMode
+ int getShortcutMenuMode() {
+ return mShortcutMenuMode;
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/TargetAdapter.java b/core/java/com/android/internal/accessibility/dialog/TargetAdapter.java
new file mode 100644
index 0000000..1efa17e
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/TargetAdapter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.Switch;
+import android.widget.TextView;
+
+/**
+ * Abstract base class for creating target adapter for chooser activity.
+ */
+abstract class TargetAdapter extends BaseAdapter {
+ static class ViewHolder{
+ CheckBox mCheckBoxView;
+ ImageView mIconView;
+ TextView mLabelView;
+ Switch mSwitchItem;
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/TargetOperations.java b/core/java/com/android/internal/accessibility/dialog/TargetOperations.java
new file mode 100644
index 0000000..77cc5b4
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/TargetOperations.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import android.annotation.NonNull;
+
+import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
+import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
+
+/**
+ * Interface definition for operations with a accessibility target that was invoked.
+ */
+interface TargetOperations {
+ /**
+ * Called when a accessibility target has been invoked and notified to update latest status.
+ */
+ void updateActionItem(@NonNull ViewHolder holder,
+ @ShortcutMenuMode int shortcutMenuMode);
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java
new file mode 100644
index 0000000..3a42f7e
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static com.android.internal.accessibility.util.AccessibilityUtils.isAccessibilityServiceEnabled;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
+import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
+import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
+
+/**
+ * Extension for {@link AccessibilityServiceTarget} with {@link AccessibilityFragmentType#TOGGLE}
+ * type.
+ */
+class ToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
+
+ ToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
+ @NonNull AccessibilityServiceInfo serviceInfo) {
+ super(context,
+ shortcutType,
+ AccessibilityFragmentType.TOGGLE,
+ serviceInfo);
+ }
+
+ @Override
+ public void updateActionItem(@NonNull ViewHolder holder,
+ @ShortcutMenuMode int shortcutMenuMode) {
+ super.updateActionItem(holder, shortcutMenuMode);
+
+ final boolean isEditMenuMode =
+ shortcutMenuMode == ShortcutMenuMode.EDIT;
+ holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
+ holder.mSwitchItem.setChecked(isAccessibilityServiceEnabled(getContext(), getId()));
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java
new file mode 100644
index 0000000..fcbf5ec
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.provider.Settings;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
+import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
+import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
+
+/**
+ * Extension for {@link AccessibilityTarget} with {@link AccessibilityFragmentType#TOGGLE}
+ * type.
+ */
+class ToggleWhiteListingFeatureTarget extends AccessibilityTarget {
+
+ ToggleWhiteListingFeatureTarget(Context context, @ShortcutType int shortcutType,
+ boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) {
+ super(context, shortcutType, AccessibilityFragmentType.TOGGLE,
+ isShortcutSwitched, id, label, icon, key);
+ }
+
+ @Override
+ public void updateActionItem(@NonNull ViewHolder holder,
+ @ShortcutMenuMode int shortcutMenuMode) {
+ super.updateActionItem(holder, shortcutMenuMode);
+
+ final boolean isEditMenuMode =
+ shortcutMenuMode == ShortcutMenuMode.EDIT;
+ holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
+ holder.mSwitchItem.setChecked(isFeatureEnabled());
+ }
+
+ private boolean isFeatureEnabled() {
+ return Settings.Secure.getInt(getContext().getContentResolver(),
+ getKey(), /* settingsValueOff */ 0) == /* settingsValueOn */ 1;
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/VolumeShortcutToggleAccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/VolumeShortcutToggleAccessibilityServiceTarget.java
new file mode 100644
index 0000000..04f5061
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/VolumeShortcutToggleAccessibilityServiceTarget.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
+
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState;
+import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.view.accessibility.AccessibilityManager.ShortcutType;
+import android.widget.Toast;
+
+import com.android.internal.R;
+import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
+
+/**
+ * Extension for {@link AccessibilityServiceTarget} with
+ * {@link AccessibilityFragmentType#VOLUME_SHORTCUT_TOGGLE} type.
+ */
+class VolumeShortcutToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
+
+ VolumeShortcutToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
+ @NonNull AccessibilityServiceInfo serviceInfo) {
+ super(context,
+ shortcutType,
+ AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE,
+ serviceInfo);
+ }
+
+ @Override
+ public void onCheckedChanged(boolean isChecked) {
+ switch (getShortcutType()) {
+ case ACCESSIBILITY_BUTTON:
+ onCheckedFromAccessibilityButton(isChecked);
+ return;
+ case ACCESSIBILITY_SHORTCUT_KEY:
+ super.onCheckedChanged(isChecked);
+ return;
+ default:
+ throw new IllegalStateException("Unexpected shortcut type");
+ }
+ }
+
+ private void onCheckedFromAccessibilityButton(boolean isChecked) {
+ setShortcutEnabled(isChecked);
+ final ComponentName componentName = ComponentName.unflattenFromString(getId());
+ setAccessibilityServiceState(getContext(), componentName, isChecked);
+
+ if (!isChecked) {
+ optOutValueFromSettings(getContext(), UserShortcutType.HARDWARE, getId());
+
+ final String warningText =
+ getContext().getString(R.string.accessibility_uncheck_legacy_item_warning,
+ getLabel());
+ Toast.makeText(getContext(), warningText, Toast.LENGTH_SHORT).show();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
index a92a50d..e50b010 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
@@ -15,6 +15,7 @@
*/
package com.android.internal.accessibility.util;
+
import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
@@ -27,9 +28,11 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.view.accessibility.AccessibilityManager;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -129,4 +132,27 @@
? AccessibilityFragmentType.INVISIBLE_TOGGLE
: AccessibilityFragmentType.TOGGLE;
}
+
+ /**
+ * Returns if a {@code componentId} service is enabled.
+ *
+ * @param context The current context.
+ * @param componentId The component id that need to be checked.
+ * @return {@code true} if a {@code componentId} service is enabled.
+ */
+ public static boolean isAccessibilityServiceEnabled(Context context,
+ @NonNull String componentId) {
+ final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
+ final List<AccessibilityServiceInfo> enabledServices =
+ am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+
+ for (AccessibilityServiceInfo info : enabledServices) {
+ final String id = info.getComponentName().flattenToString();
+ if (id.equals(componentId)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index 7ec80ec..c338a29 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -25,8 +25,10 @@
import android.content.Context;
import android.provider.Settings;
import android.text.TextUtils;
+import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.ShortcutType;
+import java.util.List;
import java.util.StringJoiner;
/**
@@ -97,29 +99,6 @@
}
/**
- * Returns if component id existed in one of {@link UserShortcutType} string from Settings.
- *
- * @param context The current context.
- * @param shortcutTypes A combination of {@link UserShortcutType}.
- * @param componentId The component id that need to be checked existed in Settings.
- * @return {@code true} if component id existed in Settings.
- */
- public static boolean hasValuesInSettings(Context context, @UserShortcutType int shortcutTypes,
- @NonNull String componentId) {
- boolean exist = false;
- if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
- exist = isComponentIdExistingInSettings(context, UserShortcutType.SOFTWARE,
- componentId);
- }
- if (((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE)) {
- exist |= isComponentIdExistingInSettings(context, UserShortcutType.HARDWARE,
- componentId);
- }
- return exist;
- }
-
-
- /**
* Returns if component id existed in Settings.
*
* @param context The current context.
@@ -149,6 +128,21 @@
}
/**
+ * Returns if a {@code shortcutType} shortcut contains {@code componentId}.
+ *
+ * @param context The current context.
+ * @param shortcutType The preferred shortcut type user selected.
+ * @param componentId The component id that need to be checked.
+ * @return {@code true} if a component id is contained.
+ */
+ public static boolean isShortcutContained(Context context, @ShortcutType int shortcutType,
+ @NonNull String componentId) {
+ final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
+ final List<String> requiredTargets = am.getAccessibilityShortcutTargets(shortcutType);
+ return requiredTargets.contains(componentId);
+ }
+
+ /**
* Converts {@link UserShortcutType} to {@link Settings.Secure} key.
*
* @param type The shortcut type.
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 73109c5..cee8a92 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -256,6 +256,7 @@
}
}
+ setPlaceholderCount(0);
int n;
if ((currentResolveList != null) && ((n = currentResolveList.size()) > 0)) {
// We only care about fixing the unfilteredList if the current resolve list and
diff --git a/core/proto/Android.bp b/core/proto/Android.bp
index 6119d71..3b891d6 100644
--- a/core/proto/Android.bp
+++ b/core/proto/Android.bp
@@ -28,13 +28,3 @@
"android/bluetooth/smp/enums.proto",
],
}
-
-java_library_host {
- name: "protolog-proto",
- srcs: [
- "android/server/protolog.proto"
- ],
- proto: {
- type: "full",
- },
-}
diff --git a/core/proto/android/app/appexitinfo.proto b/core/proto/android/app/appexitinfo.proto
index 4b9444e..cc3d367 100644
--- a/core/proto/android/app/appexitinfo.proto
+++ b/core/proto/android/app/appexitinfo.proto
@@ -41,7 +41,7 @@
optional int64 pss = 11;
optional int64 rss = 12;
optional int64 timestamp = 13;
- optional string description = 14;
+ optional string description = 14 [(.android.privacy).dest = DEST_EXPLICIT];
optional bytes state = 15;
optional string trace_file = 16;
}
diff --git a/core/proto/android/content/locusid.proto b/core/proto/android/content/locusid.proto
index 4f0ce6b..e2ba78d 100644
--- a/core/proto/android/content/locusid.proto
+++ b/core/proto/android/content/locusid.proto
@@ -20,8 +20,10 @@
option java_multiple_files = true;
+import "frameworks/base/core/proto/android/privacy.proto";
+
// On disk representation of android.content.LocusId. Currently used by
// com.android.server.people.ConversationInfoProto.
message LocusIdProto {
- optional string locus_id = 1;
+ optional string locus_id = 1 [(.android.privacy).dest = DEST_EXPLICIT];
}
diff --git a/core/proto/android/nfc/aid_group.proto b/core/proto/android/nfc/aid_group.proto
index 0636519..8810ff7 100644
--- a/core/proto/android/nfc/aid_group.proto
+++ b/core/proto/android/nfc/aid_group.proto
@@ -23,7 +23,7 @@
// Debugging information for android.nfc.cardemulation.AidGroup
message AidGroupProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ option (.android.msg_privacy).dest = DEST_EXPLICIT;
optional string category = 1;
// A group of Application Identifiers. A hexadecimal string, with an even amount of hexadecimal
diff --git a/core/proto/android/nfc/apdu_service_info.proto b/core/proto/android/nfc/apdu_service_info.proto
index c726ea3..fd110c4 100644
--- a/core/proto/android/nfc/apdu_service_info.proto
+++ b/core/proto/android/nfc/apdu_service_info.proto
@@ -25,7 +25,7 @@
// Debugging information for android.nfc.cardemulation.ApduServiceInfo
message ApduServiceInfoProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ option (.android.msg_privacy).dest = DEST_EXPLICIT;
optional .android.content.ComponentNameProto component_name = 1;
optional string description = 2;
diff --git a/core/proto/android/nfc/card_emulation.proto b/core/proto/android/nfc/card_emulation.proto
index 474d14a..9c3c6d7 100644
--- a/core/proto/android/nfc/card_emulation.proto
+++ b/core/proto/android/nfc/card_emulation.proto
@@ -75,8 +75,8 @@
message RegisteredAidCacheProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
message AidCacheEntry {
- optional string key = 1;
- optional string category = 2;
+ optional string key = 1 [(.android.privacy).dest = DEST_EXPLICIT];
+ optional string category = 2 [(.android.privacy).dest = DEST_EXPLICIT];
optional .android.content.ComponentNameProto default_component = 3;
repeated .android.nfc.cardemulation.ApduServiceInfoProto services = 4;
}
@@ -91,7 +91,7 @@
message Route {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 id = 1;
- repeated string aids = 2;
+ repeated string aids = 2 [(.android.privacy).dest = DEST_EXPLICIT];
}
optional int32 default_route = 1;
repeated Route routes = 2;
@@ -108,7 +108,7 @@
message SystemCodeRoutingManagerProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
message T3tIdentifier {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ option (.android.msg_privacy).dest = DEST_EXPLICIT;
optional string system_code = 1;
optional string nfcid2 = 2;
}
diff --git a/core/proto/android/nfc/ndef.proto b/core/proto/android/nfc/ndef.proto
index 52b3238..5e10084 100644
--- a/core/proto/android/nfc/ndef.proto
+++ b/core/proto/android/nfc/ndef.proto
@@ -31,7 +31,7 @@
message NdefRecordProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional bytes type = 1;
- optional bytes id = 2;
+ optional bytes type = 1 [(.android.privacy).dest = DEST_EXPLICIT];
+ optional bytes id = 2 [(.android.privacy).dest = DEST_EXPLICIT];
optional int32 payload_bytes = 3;
}
diff --git a/core/proto/android/nfc/nfc_fservice_info.proto b/core/proto/android/nfc/nfc_fservice_info.proto
index eecd7b0..0c4c72a 100644
--- a/core/proto/android/nfc/nfc_fservice_info.proto
+++ b/core/proto/android/nfc/nfc_fservice_info.proto
@@ -24,7 +24,7 @@
// Debugging information for android.nfc.cardemulation.NfcFServiceInfo
message NfcFServiceInfoProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ option (.android.msg_privacy).dest = DEST_EXPLICIT;
optional .android.content.ComponentNameProto component_name = 1;
// Description of the service
diff --git a/core/proto/android/nfc/nfc_service.proto b/core/proto/android/nfc/nfc_service.proto
index 73a7989..2df1d5d 100644
--- a/core/proto/android/nfc/nfc_service.proto
+++ b/core/proto/android/nfc/nfc_service.proto
@@ -104,7 +104,7 @@
optional int32 send_flags = 5;
optional bool send_enabled = 6;
optional bool receive_enabled = 7;
- optional string callback_ndef = 8;
+ optional string callback_ndef = 8 [(.android.privacy).dest = DEST_EXPLICIT];
optional .android.nfc.NdefMessageProto message_to_send = 9;
repeated string uris_to_send = 10 [(.android.privacy).dest = DEST_EXPLICIT];
}
diff --git a/core/proto/android/server/notificationhistory.proto b/core/proto/android/server/notificationhistory.proto
index 15f4abb..9896ba5 100644
--- a/core/proto/android/server/notificationhistory.proto
+++ b/core/proto/android/server/notificationhistory.proto
@@ -19,6 +19,8 @@
option java_multiple_files = true;
+import "frameworks/base/core/proto/android/privacy.proto";
+
// On disk data store for historical notifications
message NotificationHistoryProto {
message StringPool {
@@ -33,7 +35,7 @@
optional int32 package_index = 2;
// The name of the NotificationChannel this notification was posted to
- optional string channel_name = 3;
+ optional string channel_name = 3 [(.android.privacy).dest = DEST_EXPLICIT];
// channel_name_index contains the index + 1 of the channel name in the string pool
optional int32 channel_name_index = 4;
@@ -49,9 +51,9 @@
// The time at which the notification was posted
optional int64 posted_time_ms = 9;
// The title of the notification
- optional string title = 10;
+ optional string title = 10 [(.android.privacy).dest = DEST_EXPLICIT];
// The text of the notification
- optional string text = 11;
+ optional string text = 11 [(.android.privacy).dest = DEST_EXPLICIT];
// The small icon of the notification
optional Icon icon = 12;
diff --git a/core/proto/android/server/peopleservice.proto b/core/proto/android/server/peopleservice.proto
index e65a2ab..59556c4 100644
--- a/core/proto/android/server/peopleservice.proto
+++ b/core/proto/android/server/peopleservice.proto
@@ -21,6 +21,7 @@
option java_multiple_files = true;
import "frameworks/base/core/proto/android/content/locusid.proto";
+import "frameworks/base/core/proto/android/privacy.proto";
// On disk data of conversation infos for a user and app package.
message ConversationInfosProto {
@@ -40,10 +41,10 @@
optional .android.content.LocusIdProto locus_id_proto = 2;
// The URI of the contact in the conversation.
- optional string contact_uri = 3;
+ optional string contact_uri = 3 [(.android.privacy).dest = DEST_EXPLICIT];
// The notification channel id of the conversation.
- optional string notification_channel_id = 4;
+ optional string notification_channel_id = 4 [(.android.privacy).dest = DEST_EXPLICIT];
// Integer representation of shortcut bit flags.
optional int32 shortcut_flags = 5;
@@ -52,7 +53,7 @@
optional int32 conversation_flags = 6;
// The phone number of the contact.
- optional string contact_phone_number = 7;
+ optional string contact_phone_number = 7 [(.android.privacy).dest = DEST_EXPLICIT];
}
// On disk data of events.
diff --git a/core/proto/android/server/protolog.proto b/core/proto/android/server/protolog.proto
index 3512c0a..34dc55b 100644
--- a/core/proto/android/server/protolog.proto
+++ b/core/proto/android/server/protolog.proto
@@ -20,8 +20,12 @@
option java_multiple_files = true;
+import "frameworks/base/core/proto/android/privacy.proto";
+
/* represents a single log entry */
message ProtoLogMessage {
+ option (.android.msg_privacy).dest = DEST_LOCAL;
+
/* log statement identifier, created from message string and log level. */
optional sfixed32 message_hash = 1;
/* log time, relative to the elapsed system time clock. */
@@ -40,6 +44,8 @@
Encoded, it should start with 0x9 0x50 0x52 0x4f 0x54 0x4f 0x4c 0x4f 0x47 (.PROTOLOG), such
that they can be easily identified. */
message ProtoLogFileProto {
+ option (.android.msg_privacy).dest = DEST_LOCAL;
+
/* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
(this is needed because enums have to be 32 bits and there's no nice way to put 64bit
constants into .proto files. */
diff --git a/core/proto/android/server/syncstorageengine.proto b/core/proto/android/server/syncstorageengine.proto
index 87eb1b3..d313747 100644
--- a/core/proto/android/server/syncstorageengine.proto
+++ b/core/proto/android/server/syncstorageengine.proto
@@ -58,7 +58,7 @@
message LastEventInfo {
optional int64 last_event_time = 1; // time since epoch
- optional string last_event = 2;
+ optional string last_event = 2 [(.android.privacy).dest = DEST_EXPLICIT];
}
// Note: version doesn't need to be stored in proto because of how protos store information but
@@ -68,7 +68,7 @@
optional int32 last_success_source = 4;
optional int64 last_failure_time = 5; // time since epoch
optional int32 last_failure_source = 6;
- optional string last_failure_message = 7;
+ optional string last_failure_message = 7 [(.android.privacy).dest = DEST_EXPLICIT];
optional int64 initial_failure_time = 8; // time since epoch
optional bool pending = 9;
optional bool initialize = 10;
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
index 24b0728..664c22d 100644
--- a/core/proto/android/server/usagestatsservice_v2.proto
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -112,13 +112,13 @@
optional int32 flags = 4;
optional int32 type = 5;
optional .android.content.ConfigurationProto config = 6;
- optional string shortcut_id = 7;
+ optional string shortcut_id = 7 [(.android.privacy).dest = DEST_EXPLICIT];
optional int32 standby_bucket = 8;
- optional string notification_channel_id = 9;
+ optional string notification_channel_id = 9 [(.android.privacy).dest = DEST_EXPLICIT];
optional int32 instance_id = 10;
optional string task_root_package = 11;
optional string task_root_class = 12;
- optional string locus_id = 13;
+ optional string locus_id = 13 [(.android.privacy).dest = DEST_EXPLICIT];
}
/**
@@ -128,7 +128,7 @@
message PackagesMap {
optional int32 package_token = 1;
// The list of strings for each package where their indices are the token
- repeated string strings = 2;
+ repeated string strings = 2 [(.android.privacy).dest = DEST_EXPLICIT];
}
optional int32 counter = 1;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 55ea3159..0f5616f 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -310,7 +310,7 @@
optional bool visible = 24;
reserved 25; // configuration_container
optional IdentifierProto identifier = 26;
- optional string state = 27;
+ optional string state = 27 [(.android.privacy).dest = DEST_EXPLICIT];
optional bool front_of_task = 28;
optional int32 proc_id = 29;
optional bool translucent = 30;
diff --git a/core/proto/android/service/sensor_service.proto b/core/proto/android/service/sensor_service.proto
index 48f6670..7f60534 100644
--- a/core/proto/android/service/sensor_service.proto
+++ b/core/proto/android/service/sensor_service.proto
@@ -166,7 +166,7 @@
message RecentEventsLog {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
- optional string name = 1;
+ optional string name = 1 [(.android.privacy).dest = DEST_EXPLICIT];
optional int32 recent_events_count = 2;
repeated Event events = 3;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5d6fc76..6e8b9de 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -526,6 +526,7 @@
<protected-broadcast android:name="com.android.settings.wifi.action.NETWORK_REQUEST" />
<protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
+ <protected-broadcast android:name="NotificationHistoryDatabase.CLEANUP" />
<protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
<protected-broadcast android:name="EventConditionProvider.EVALUATE" />
<protected-broadcast android:name="SnoozeHelper.EVALUATE" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c9c498e..0e40c98 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3897,6 +3897,19 @@
information. -->
<string name="gpsVerifNo">No</string>
+ <!-- Notification title shown to user to inform them their device location was accessed by
+ an external entity during an emergency (usually an emergency phone call).
+ [CHAR LIMIT=40] -->
+ <string name="gnss_nfw_notification_title">Emergency location accessed</string>
+ <!-- Notification message shown to user to inform them their device location was accessed by
+ something OEM related during an emergency (usually an emergency phone call).
+ [CHAR LIMIT=NONE] -->
+ <string name="gnss_nfw_notification_message_oem">Your device manufacturer accessed your location during a recent emergency session</string>
+ <!-- Notification message shown to user to inform them their device location was accessed by
+ something carrier related during an emergency (usually an emergency phone call).
+ [CHAR LIMIT=NONE] -->
+ <string name="gnss_nfw_notification_message_carrier">Your carrier accessed your location during a recent emergency session</string>
+
<!-- Error message when the sync tried to delete too many things -->
<string name="sync_too_many_deletes">Delete limit exceeded</string>
<!-- Dialog message for when there are too many deletes that would take place and we want user confirmation -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 717f326d..6178fda 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -740,6 +740,9 @@
<java-symbol type="string" name="gpsNotifTitle" />
<java-symbol type="string" name="gpsVerifNo" />
<java-symbol type="string" name="gpsVerifYes" />
+ <java-symbol type="string" name="gnss_nfw_notification_title" />
+ <java-symbol type="string" name="gnss_nfw_notification_message_carrier" />
+ <java-symbol type="string" name="gnss_nfw_notification_message_oem" />
<java-symbol type="string" name="gsm_alphabet_default_charset" />
<java-symbol type="string" name="httpError" />
<java-symbol type="string" name="httpErrorAuth" />
diff --git a/core/tests/coretests/res/raw/install_app1_cert5 b/core/tests/coretests/res/raw/install_app1_cert5
new file mode 100644
index 0000000..138b611
--- /dev/null
+++ b/core/tests/coretests/res/raw/install_app1_cert5
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app1_cert5_rotated_cert6 b/core/tests/coretests/res/raw/install_app1_cert5_rotated_cert6
new file mode 100644
index 0000000..2da2436
--- /dev/null
+++ b/core/tests/coretests/res/raw/install_app1_cert5_rotated_cert6
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app1_cert6 b/core/tests/coretests/res/raw/install_app1_cert6
new file mode 100644
index 0000000..256e03a
--- /dev/null
+++ b/core/tests/coretests/res/raw/install_app1_cert6
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app2_cert5_rotated_cert6 b/core/tests/coretests/res/raw/install_app2_cert5_rotated_cert6
new file mode 100644
index 0000000..30bb647
--- /dev/null
+++ b/core/tests/coretests/res/raw/install_app2_cert5_rotated_cert6
Binary files differ
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index ed2436a..79cb1f9 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -1830,27 +1830,35 @@
* The following series of tests are related to upgrading apps with
* different certificates.
*/
- private int APP1_UNSIGNED = R.raw.install_app1_unsigned;
+ private static final int APP1_UNSIGNED = R.raw.install_app1_unsigned;
- private int APP1_CERT1 = R.raw.install_app1_cert1;
+ private static final int APP1_CERT1 = R.raw.install_app1_cert1;
- private int APP1_CERT2 = R.raw.install_app1_cert2;
+ private static final int APP1_CERT2 = R.raw.install_app1_cert2;
- private int APP1_CERT1_CERT2 = R.raw.install_app1_cert1_cert2;
+ private static final int APP1_CERT1_CERT2 = R.raw.install_app1_cert1_cert2;
- private int APP1_CERT3_CERT4 = R.raw.install_app1_cert3_cert4;
+ private static final int APP1_CERT3_CERT4 = R.raw.install_app1_cert3_cert4;
- private int APP1_CERT3 = R.raw.install_app1_cert3;
+ private static final int APP1_CERT3 = R.raw.install_app1_cert3;
- private int APP2_UNSIGNED = R.raw.install_app2_unsigned;
+ private static final int APP1_CERT5 = R.raw.install_app1_cert5;
- private int APP2_CERT1 = R.raw.install_app2_cert1;
+ private static final int APP1_CERT5_ROTATED_CERT6 = R.raw.install_app1_cert5_rotated_cert6;
- private int APP2_CERT2 = R.raw.install_app2_cert2;
+ private static final int APP1_CERT6 = R.raw.install_app1_cert6;
- private int APP2_CERT1_CERT2 = R.raw.install_app2_cert1_cert2;
+ private static final int APP2_UNSIGNED = R.raw.install_app2_unsigned;
- private int APP2_CERT3 = R.raw.install_app2_cert3;
+ private static final int APP2_CERT1 = R.raw.install_app2_cert1;
+
+ private static final int APP2_CERT2 = R.raw.install_app2_cert2;
+
+ private static final int APP2_CERT1_CERT2 = R.raw.install_app2_cert1_cert2;
+
+ private static final int APP2_CERT3 = R.raw.install_app2_cert3;
+
+ private static final int APP2_CERT5_ROTATED_CERT6 = R.raw.install_app2_cert5_rotated_cert6;
private InstallParams replaceCerts(int apk1, int apk2, boolean cleanUp, boolean fail,
int retCode) throws Exception {
@@ -2473,6 +2481,26 @@
}
@LargeTest
+ public void testCheckSignaturesRotatedAgainstOriginal() throws Exception {
+ // checkSignatures should be backwards compatible with pre-rotation behavior; this test
+ // verifies that an app signed with a rotated key results in a signature match with an app
+ // signed with the original key in the lineage.
+ int apk1 = APP1_CERT5;
+ int apk2 = APP2_CERT5_ROTATED_CERT6;
+ checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
+ }
+
+ @LargeTest
+ public void testCheckSignaturesRotatedAgainstRotated() throws Exception {
+ // checkSignatures should be successful when both apps have been signed with the same
+ // rotated key since the initial signature comparison between the two apps should
+ // return a match.
+ int apk1 = APP1_CERT5_ROTATED_CERT6;
+ int apk2 = APP2_CERT5_ROTATED_CERT6;
+ checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
+ }
+
+ @LargeTest
public void testInstallNoCertificates() throws Exception {
int apk1 = APP1_UNSIGNED;
String apk1Name = "install1.apk";
diff --git a/core/tests/overlaytests/host/TEST_MAPPING b/core/tests/overlaytests/host/TEST_MAPPING
new file mode 100644
index 0000000..e0c03e0
--- /dev/null
+++ b/core/tests/overlaytests/host/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name" : "OverlayHostTests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 405410a..97d32d8 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -897,7 +897,7 @@
synchronized (mSessionMap) {
if (mSessionMap.get(session) != null) {
- mTunerResourceManager.releaseCasSession(mSessionMap.get(session));
+ mTunerResourceManager.releaseCasSession(mSessionMap.get(session), mClientId);
mSessionMap.remove(session);
}
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 861eeea..ee5ac46 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -263,14 +263,14 @@
}
private void setFrontendInfoList() {
- List<Integer> ids = nativeGetFrontendIds();
+ List<Integer> ids = getFrontendIds();
if (ids == null) {
return;
}
TunerFrontendInfo[] infos = new TunerFrontendInfo[ids.size()];
for (int i = 0; i < ids.size(); i++) {
int id = ids.get(i);
- FrontendInfo frontendInfo = nativeGetFrontendInfo(id);
+ FrontendInfo frontendInfo = getFrontendInfoById(id);
if (frontendInfo == null) {
continue;
}
@@ -281,6 +281,11 @@
mTunerResourceManager.setFrontendInfoList(infos);
}
+ /** @hide */
+ public List<Integer> getFrontendIds() {
+ return nativeGetFrontendIds();
+ }
+
private void setLnbIds() {
int[] ids = nativeGetLnbIds();
if (ids == null) {
@@ -345,14 +350,17 @@
@Override
public void close() {
if (mFrontendHandle != null) {
- mTunerResourceManager.releaseFrontend(mFrontendHandle);
+ nativeCloseFrontendByHandle(mFrontendHandle);
+ mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
mFrontendHandle = null;
+ mFrontend = null;
}
if (mLnb != null) {
- mTunerResourceManager.releaseLnb(mLnbHandle);
+ mTunerResourceManager.releaseLnb(mLnbHandle, mClientId);
mLnb = null;
+ mLnbHandle = null;
}
- nativeClose();
+ TunerUtils.throwExceptionForResult(nativeClose(), "failed to close tuner");
}
/**
@@ -374,6 +382,8 @@
* Native method to open frontend of the given ID.
*/
private native Frontend nativeOpenFrontendByHandle(int handle);
+ @Result
+ private native int nativeCloseFrontendByHandle(int handle);
private native int nativeTune(int type, FrontendSettings settings);
private native int nativeStopTune();
private native int nativeScan(int settingsType, FrontendSettings settings, int scanType);
@@ -522,6 +532,7 @@
public int tune(@NonNull FrontendSettings settings) {
mFrontendType = settings.getType();
checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
+
mFrontendInfo = null;
return nativeTune(settings.getType(), settings);
}
@@ -706,11 +717,16 @@
throw new IllegalStateException("frontend is not initialized");
}
if (mFrontendInfo == null) {
- mFrontendInfo = nativeGetFrontendInfo(mFrontend.mId);
+ mFrontendInfo = getFrontendInfoById(mFrontend.mId);
}
return mFrontendInfo;
}
+ /** @hide */
+ public FrontendInfo getFrontendInfoById(int id) {
+ return nativeGetFrontendInfo(id);
+ }
+
/**
* Gets Demux capabilities.
*
diff --git a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 77cac6e..7077cd1 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -85,7 +85,8 @@
* Updates the available Frontend resources information on the current device.
*
* <p><strong>Note:</strong> This update must happen before the first
- * {@link #requestFrontend(TunerFrontendRequest,int[])} and {@link #releaseFrontend(int)} call.
+ * {@link #requestFrontend(TunerFrontendRequest,int[])} and {@link #releaseFrontend(int, int)}
+ * call.
*
* @param infos an array of the available {@link TunerFrontendInfo} information.
*/
@@ -95,7 +96,8 @@
* Updates the available Cas resource information on the current device.
*
* <p><strong>Note:</strong> This update must happen before the first
- * {@link #requestCasSession(CasSessionRequest, int[])} and {@link #releaseCasSession(int)} call.
+ * {@link #requestCasSession(CasSessionRequest, int[])} and {@link #releaseCasSession(int, int)}
+ * call.
*
* @param casSystemId id of the updating CAS system.
* @param maxSessionNum the max session number of the CAS system that is updated.
@@ -106,7 +108,7 @@
* Updates the available Lnb resource information on the current device.
*
* <p><strong>Note:</strong> This update must happen before the first
- * {@link #requestLnb(TunerLnbRequest, int[])} and {@link #releaseLnb(int)} call.
+ * {@link #requestLnb(TunerLnbRequest, int[])} and {@link #releaseLnb(int, int)} call.
*
* @param lnbIds ids of the updating lnbs.
*/
@@ -132,11 +134,11 @@
* before this request.
*
* @param request {@link TunerFrontendRequest} information of the current request.
- * @param frontendId a one-element array to return the granted frontendId.
+ * @param frontendHandle a one-element array to return the granted frontendHandle.
*
* @return true if there is frontend granted.
*/
- boolean requestFrontend(in TunerFrontendRequest request, out int[] frontendId);
+ boolean requestFrontend(in TunerFrontendRequest request, out int[] frontendHandle);
/*
* Requests to share frontend with an existing client.
@@ -240,11 +242,11 @@
* <p><strong>Note:</strong> {@link #setLnbInfos(int[])} must be called before this request.
*
* @param request {@link TunerLnbRequest} information of the current request.
- * @param lnbId a one-element array to return the granted Lnb id.
+ * @param lnbHandle a one-element array to return the granted Lnb handle.
*
* @return true if there is Lnb granted.
*/
- boolean requestLnb(in TunerLnbRequest request, out int[] lnbId);
+ boolean requestLnb(in TunerLnbRequest request, out int[] lnbHandle);
/*
* Notifies the TRM that the given frontend has been released.
@@ -254,9 +256,10 @@
* <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called
* before this release.
*
- * @param frontendId the id of the released frontend.
+ * @param frontendHandle the handle of the released frontend.
+ * @param clientId the id of the client that is releasing the frontend.
*/
- void releaseFrontend(in int frontendId);
+ void releaseFrontend(in int frontendHandle, int clientId);
/*
* Notifies the TRM that the Demux with the given handle was released.
@@ -264,8 +267,9 @@
* <p>Client must call this whenever it releases a demux.
*
* @param demuxHandle the handle of the released Tuner Demux.
+ * @param clientId the id of the client that is releasing the demux.
*/
- void releaseDemux(in int demuxHandle);
+ void releaseDemux(in int demuxHandle, int clientId);
/*
* Notifies the TRM that the Descrambler with the given handle was released.
@@ -273,8 +277,9 @@
* <p>Client must call this whenever it releases a descrambler.
*
* @param demuxHandle the handle of the released Tuner Descrambler.
+ * @param clientId the id of the client that is releasing the descrambler.
*/
- void releaseDescrambler(in int descramblerHandle);
+ void releaseDescrambler(in int descramblerHandle, int clientId);
/*
* Notifies the TRM that the given Cas session has been released.
@@ -284,19 +289,21 @@
* <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this release.
*
* @param sessionResourceId the id of the released CAS session.
+ * @param clientId the id of the client that is releasing the cas session.
*/
- void releaseCasSession(in int sessionResourceId);
+ void releaseCasSession(in int sessionResourceId, int clientId);
/*
- * Notifies the TRM that the Lnb with the given id was released.
+ * Notifies the TRM that the Lnb with the given handle was released.
*
* <p>Client must call this whenever it releases an Lnb.
*
* <p><strong>Note:</strong> {@link #setLnbInfos(int[])} must be called before this release.
*
- * @param lnbId the id of the released Tuner Lnb.
+ * @param lnbHandle the handle of the released Tuner Lnb.
+ * @param clientId the id of the client that is releasing the lnb.
*/
- void releaseLnb(in int lnbId);
+ void releaseLnb(in int lnbHandle, int clientId);
/*
* Compare two clients' priority.
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index 2c8899c..b4dcc5d 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -64,6 +64,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final int INVALID_RESOURCE_HANDLE = -1;
+ public static final int INVALID_OWNER_ID = -1;
/**
* Tuner resource type to help generate resource handle
*/
@@ -73,6 +74,7 @@
TUNER_RESOURCE_TYPE_DESCRAMBLER,
TUNER_RESOURCE_TYPE_LNB,
TUNER_RESOURCE_TYPE_CAS_SESSION,
+ TUNER_RESOURCE_TYPE_MAX,
})
@Retention(RetentionPolicy.SOURCE)
public @interface TunerResourceType {}
@@ -82,6 +84,7 @@
public static final int TUNER_RESOURCE_TYPE_DESCRAMBLER = 2;
public static final int TUNER_RESOURCE_TYPE_LNB = 3;
public static final int TUNER_RESOURCE_TYPE_CAS_SESSION = 4;
+ public static final int TUNER_RESOURCE_TYPE_MAX = 5;
private final ITunerResourceManager mService;
private final int mUserId;
@@ -177,7 +180,8 @@
* Updates the current TRM of the TunerHAL Frontend information.
*
* <p><strong>Note:</strong> This update must happen before the first
- * {@link #requestFrontend(TunerFrontendRequest, int[])} and {@link #releaseFrontend(int)} call.
+ * {@link #requestFrontend(TunerFrontendRequest, int[])} and
+ * {@link #releaseFrontend(int, int)} call.
*
* @param infos an array of the available {@link TunerFrontendInfo} information.
*/
@@ -193,7 +197,7 @@
* Updates the TRM of the current CAS information.
*
* <p><strong>Note:</strong> This update must happen before the first
- * {@link #requestCasSession(CasSessionRequest, int[])} and {@link #releaseCasSession(int)}
+ * {@link #requestCasSession(CasSessionRequest, int[])} and {@link #releaseCasSession(int, int)}
* call.
*
* @param casSystemId id of the updating CAS system.
@@ -211,7 +215,7 @@
* Updates the TRM of the current Lnb information.
*
* <p><strong>Note:</strong> This update must happen before the first
- * {@link #requestLnb(TunerLnbRequest, int[])} and {@link #releaseLnb(int)} call.
+ * {@link #requestLnb(TunerLnbRequest, int[])} and {@link #releaseLnb(int, int)} call.
*
* @param lnbIds ids of the updating lnbs.
*/
@@ -243,16 +247,16 @@
* before this request.
*
* @param request {@link TunerFrontendRequest} information of the current request.
- * @param frontendId a one-element array to return the granted frontendId. If
- * no frontend granted, this will return {@link #INVALID_FRONTEND_ID}.
+ * @param frontendHandle a one-element array to return the granted frontendHandle. If
+ * no frontend granted, this will return {@link #INVALID_RESOURCE_HANDLE}.
*
* @return true if there is frontend granted.
*/
public boolean requestFrontend(@NonNull TunerFrontendRequest request,
- @Nullable int[] frontendId) {
+ @Nullable int[] frontendHandle) {
boolean result = false;
try {
- result = mService.requestFrontend(request, frontendId);
+ result = mService.requestFrontend(request, frontendHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -393,15 +397,15 @@
* <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this request.
*
* @param request {@link TunerLnbRequest} information of the current request.
- * @param lnbId a one-element array to return the granted Lnb id.
- * If no Lnb granted, this will return {@link #INVALID_LNB_ID}.
+ * @param lnbHandle a one-element array to return the granted Lnb handle.
+ * If no Lnb granted, this will return {@link #INVALID_RESOURCE_HANDLE}.
*
* @return true if there is Lnb granted.
*/
- public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbId) {
+ public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) {
boolean result = false;
try {
- result = mService.requestLnb(request, lnbId);
+ result = mService.requestLnb(request, lnbHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -416,11 +420,12 @@
* <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called
* before this release.
*
- * @param frontendId the id of the released frontend.
+ * @param frontendHandle the handle of the released frontend.
+ * @param clientId the id of the client that is releasing the frontend.
*/
- public void releaseFrontend(int frontendId) {
+ public void releaseFrontend(int frontendHandle, int clientId) {
try {
- mService.releaseFrontend(frontendId);
+ mService.releaseFrontend(frontendHandle, clientId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -432,10 +437,11 @@
* <p>Client must call this whenever it releases an Demux.
*
* @param demuxHandle the handle of the released Tuner Demux.
+ * @param clientId the id of the client that is releasing the demux.
*/
- public void releaseDemux(int demuxHandle) {
+ public void releaseDemux(int demuxHandle, int clientId) {
try {
- mService.releaseDemux(demuxHandle);
+ mService.releaseDemux(demuxHandle, clientId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -447,10 +453,11 @@
* <p>Client must call this whenever it releases an Descrambler.
*
* @param descramblerHandle the handle of the released Tuner Descrambler.
+ * @param clientId the id of the client that is releasing the descrambler.
*/
- public void releaseDescrambler(int descramblerHandle) {
+ public void releaseDescrambler(int descramblerHandle, int clientId) {
try {
- mService.releaseDescrambler(descramblerHandle);
+ mService.releaseDescrambler(descramblerHandle, clientId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -465,10 +472,11 @@
* release.
*
* @param sessionResourceId the id of the released CAS session.
+ * @param clientId the id of the client that is releasing the cas session.
*/
- public void releaseCasSession(int sessionResourceId) {
+ public void releaseCasSession(int sessionResourceId, int clientId) {
try {
- mService.releaseCasSession(sessionResourceId);
+ mService.releaseCasSession(sessionResourceId, clientId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -481,11 +489,12 @@
*
* <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this release.
*
- * @param lnbId the id of the released Tuner Lnb.
+ * @param lnbHandle the handle of the released Tuner Lnb.
+ * @param clientId the id of the client that is releasing the lnb.
*/
- public void releaseLnb(int lnbId) {
+ public void releaseLnb(int lnbHandle, int clientId) {
try {
- mService.releaseLnb(lnbId);
+ mService.releaseLnb(lnbHandle, clientId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 7579ca5..909394f 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -833,6 +833,12 @@
}
JTuner::~JTuner() {
+ if (mFe != NULL) {
+ mFe->close();
+ }
+ if (mDemux != NULL) {
+ mDemux->close();
+ }
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(mObject);
@@ -908,6 +914,14 @@
(jint) jId);
}
+jint JTuner::closeFrontendById(int id) {
+ if (mFe != NULL && mFeId == id) {
+ Result r = mFe->close();
+ return (jint) r;
+ }
+ return (jint) Result::SUCCESS;
+}
+
jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
@@ -1271,6 +1285,23 @@
return res;
}
+jint JTuner::close() {
+ Result res = Result::SUCCESS;
+ if (mFe != NULL) {
+ res = mFe->close();
+ if (res != Result::SUCCESS) {
+ return (jint) res;
+ }
+ }
+ if (mDemux != NULL) {
+ res = mDemux->close();
+ if (res != Result::SUCCESS) {
+ return (jint) res;
+ }
+ }
+ return (jint) res;
+}
+
jobject JTuner::getAvSyncHwId(sp<Filter> filter) {
if (mDemux == NULL) {
return NULL;
@@ -2362,6 +2393,13 @@
return tuner->openFrontendById(id);
}
+static jint android_media_tv_Tuner_close_frontend_by_handle(
+ JNIEnv *env, jobject thiz, jint handle) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ uint32_t id = getResourceIdFromHandle(handle);
+ return tuner->closeFrontendById(id);
+}
+
static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->tune(getFrontendSettings(env, type, settings));
@@ -3135,6 +3173,11 @@
return (jint) tuner->openDemux();
}
+static jint android_media_tv_Tuner_close_tuner(JNIEnv* env, jobject thiz) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return (jint) tuner->close();
+}
+
static jint android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
sp<Dvr> dvrSp = getDvr(env, dvr);
if (dvrSp == NULL) {
@@ -3424,6 +3467,8 @@
(void *)android_media_tv_Tuner_get_frontend_ids },
{ "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
(void *)android_media_tv_Tuner_open_frontend_by_handle },
+ { "nativeCloseFrontendByHandle", "(I)I",
+ (void *)android_media_tv_Tuner_close_frontend_by_handle },
{ "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
@@ -3460,6 +3505,7 @@
{ "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;",
(void *)android_media_tv_Tuner_get_demux_caps },
{ "nativeOpenDemuxByhandle", "(I)I", (void *)android_media_tv_Tuner_open_demux },
+ {"nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner },
};
static const JNINativeMethod gFilterMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 6749ba0..750b146 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -172,6 +172,7 @@
int disconnectCiCam();
jobject getFrontendIds();
jobject openFrontendById(int id);
+ jint closeFrontendById(int id);
jobject getFrontendInfo(int id);
int tune(const FrontendSettings& settings);
int stopTune();
@@ -189,6 +190,7 @@
jobject getDemuxCaps();
jobject getFrontendStatus(jintArray types);
Result openDemux();
+ jint close();
protected:
virtual ~JTuner();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 7b39ba3..345a649 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -76,7 +76,7 @@
callbackHandler.post(callback);
}
}
- });
+ }, null /* finishedListener */);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 5c3d17c..502c078 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -19,6 +19,7 @@
import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -30,12 +31,14 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
+import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Slog;
import android.util.TypedValue;
import android.view.LayoutInflater;
@@ -44,6 +47,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -63,6 +67,8 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.InjectionInflationController;
+import java.util.List;
+
public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final String TAG = "KeyguardSecurityView";
@@ -114,6 +120,47 @@
private boolean mIsDragging;
private float mStartTouchY = -1;
+ private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
+ new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+
+ private final Rect mInitialBounds = new Rect();
+ private final Rect mFinalBounds = new Rect();
+
+ @Override
+ public void onPrepare(WindowInsetsAnimation animation) {
+ mSecurityViewFlipper.getBoundsOnScreen(mInitialBounds);
+ }
+
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(WindowInsetsAnimation animation,
+ WindowInsetsAnimation.Bounds bounds) {
+ mSecurityViewFlipper.getBoundsOnScreen(mFinalBounds);
+ return bounds;
+ }
+
+ @Override
+ public WindowInsets onProgress(WindowInsets windowInsets,
+ List<WindowInsetsAnimation> list) {
+ int translationY = 0;
+ for (WindowInsetsAnimation animation : list) {
+ if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
+ continue;
+ }
+ final int paddingBottom = (int) MathUtils.lerp(
+ mInitialBounds.bottom - mFinalBounds.bottom, 0,
+ animation.getInterpolatedFraction());
+ translationY += paddingBottom;
+ }
+ mSecurityViewFlipper.setTranslationY(translationY);
+ return windowInsets;
+ }
+
+ @Override
+ public void onEnd(WindowInsetsAnimation animation) {
+ mSecurityViewFlipper.setTranslationY(0);
+ }
+ };
+
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
public boolean dismiss(boolean authenticated, int targetUserId,
@@ -162,6 +209,7 @@
if (mCurrentSecuritySelection != SecurityMode.None) {
getSecurityView(mCurrentSecuritySelection).onResume(reason);
}
+ mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry();
}
@@ -175,6 +223,7 @@
if (mCurrentSecuritySelection != SecurityMode.None) {
getSecurityView(mCurrentSecuritySelection).onPause();
}
+ mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
}
@Override
@@ -333,7 +382,9 @@
}
}
- protected void onFinishInflate() {
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
mSecurityViewFlipper = findViewById(R.id.view_flipper);
mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 9bb253b..2c1bd21 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -195,9 +195,9 @@
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@VisibleForTesting
- protected ArrayList<Action> mItems;
+ protected final ArrayList<Action> mItems = new ArrayList<>();
@VisibleForTesting
- protected ArrayList<Action> mOverflowItems;
+ protected final ArrayList<Action> mOverflowItems = new ArrayList<>();
private ActionsDialog mDialog;
@@ -453,19 +453,12 @@
prepareDialog();
seedFavorites();
- // If we only have 1 item and it's a simple press action, just do this action.
- if (mAdapter.getCount() == 1
- && mAdapter.getItem(0) instanceof SinglePressAction
- && !(mAdapter.getItem(0) instanceof LongPressAction)) {
- ((SinglePressAction) mAdapter.getItem(0)).onPress();
- } else {
- WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
- attrs.setTitle("ActionsDialog");
- attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mDialog.getWindow().setAttributes(attrs);
- mDialog.show();
- mWindowManagerFuncs.onGlobalActionsShown();
- }
+ WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
+ attrs.setTitle("ActionsDialog");
+ attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mDialog.getWindow().setAttributes(attrs);
+ mDialog.show();
+ mWindowManagerFuncs.onGlobalActionsShown();
}
@VisibleForTesting
@@ -485,7 +478,7 @@
*/
@VisibleForTesting
protected int getMaxShownPowerItems() {
- if (shouldShowControls()) {
+ if (shouldUseControlsLayout()) {
return mResources.getInteger(com.android.systemui.R.integer.power_menu_max_columns);
} else {
return Integer.MAX_VALUE;
@@ -497,10 +490,10 @@
* whether controls are enabled and whether the max number of shown items has been reached.
*/
private void addActionItem(Action action) {
- if (mItems != null && shouldShowAction(action)) {
+ if (shouldShowAction(action)) {
if (mItems.size() < getMaxShownPowerItems()) {
mItems.add(action);
- } else if (mOverflowItems != null) {
+ } else {
mOverflowItems.add(action);
}
}
@@ -522,8 +515,8 @@
mAirplaneModeOn = new AirplaneModeAction();
onAirplaneModeChanged();
- mItems = new ArrayList<Action>();
- mOverflowItems = new ArrayList<Action>();
+ mItems.clear();
+ mOverflowItems.clear();
String[] defaultActions = getDefaultActions();
// make sure emergency affordance action is first, if needed
@@ -588,6 +581,11 @@
}
}
+ private void onRotate() {
+ // re-allocate actions between main and overflow lists
+ this.createActionItems();
+ }
+
/**
* Create the global actions dialog.
*
@@ -599,11 +597,12 @@
mAdapter = new MyAdapter();
mOverflowAdapter = new MyOverflowAdapter();
- mDepthController.setShowingHomeControls(shouldShowControls());
+ mDepthController.setShowingHomeControls(shouldUseControlsLayout());
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, mOverflowAdapter,
getWalletPanelViewController(), mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- shouldShowControls() ? mControlsUiController : null, mBlurUtils);
+ shouldShowControls() ? mControlsUiController : null, mBlurUtils,
+ shouldUseControlsLayout(), this::onRotate);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.setKeyguardShowing(mKeyguardShowing);
@@ -704,7 +703,7 @@
@Override
public boolean shouldBeSeparated() {
- return !shouldShowControls();
+ return !shouldUseControlsLayout();
}
@Override
@@ -712,7 +711,7 @@
Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
View v = super.create(context, convertView, parent, inflater);
int textColor;
- if (shouldShowControls()) {
+ if (shouldUseControlsLayout()) {
v.setBackgroundTintList(ColorStateList.valueOf(v.getResources().getColor(
com.android.systemui.R.color.global_actions_emergency_background)));
textColor = v.getResources().getColor(
@@ -1152,7 +1151,7 @@
}
private int getActionLayoutId() {
- if (shouldShowControls()) {
+ if (shouldUseControlsLayout()) {
return com.android.systemui.R.layout.global_actions_grid_item_v2;
}
return com.android.systemui.R.layout.global_actions_grid_item;
@@ -1275,12 +1274,12 @@
public class MyOverflowAdapter extends BaseAdapter {
@Override
public int getCount() {
- return mOverflowItems != null ? mOverflowItems.size() : 0;
+ return mOverflowItems.size();
}
@Override
public Action getItem(int position) {
- return mOverflowItems != null ? mOverflowItems.get(position) : null;
+ return mOverflowItems.get(position);
}
@Override
@@ -1888,7 +1887,9 @@
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final NotificationShadeDepthController mDepthController;
private final BlurUtils mBlurUtils;
+ private final boolean mUseControlsLayout;
private ListPopupWindow mOverflowPopup;
+ private final Runnable mOnRotateCallback;
private ControlsUiController mControlsUiController;
private ViewGroup mControlsView;
@@ -1898,7 +1899,8 @@
NotificationShadeDepthController depthController,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- ControlsUiController controlsUiController, BlurUtils blurUtils) {
+ ControlsUiController controlsUiController, BlurUtils blurUtils,
+ boolean useControlsLayout, Runnable onRotateCallback) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
mContext = context;
mAdapter = adapter;
@@ -1909,6 +1911,8 @@
mNotificationShadeWindowController = notificationShadeWindowController;
mControlsUiController = controlsUiController;
mBlurUtils = blurUtils;
+ mUseControlsLayout = useControlsLayout;
+ mOnRotateCallback = onRotateCallback;
// Window initialization
Window window = getWindow();
@@ -2068,7 +2072,7 @@
}
if (mBackgroundDrawable == null) {
mBackgroundDrawable = new ScrimDrawable();
- if (mControlsUiController != null) {
+ if (mUseControlsLayout) {
mScrimAlpha = 1.0f;
} else {
mScrimAlpha = mBlurUtils.supportsBlursOnWindows()
@@ -2088,7 +2092,7 @@
}
private int getGlobalActionsLayoutId(Context context) {
- if (mControlsUiController != null) {
+ if (mUseControlsLayout) {
return com.android.systemui.R.layout.global_actions_grid_v2;
}
@@ -2133,9 +2137,9 @@
if (!(mBackgroundDrawable instanceof ScrimDrawable)) {
return;
}
- boolean hasControls = mControlsUiController != null;
((ScrimDrawable) mBackgroundDrawable).setColor(
- !hasControls && colors.supportsDarkText() ? Color.WHITE : Color.BLACK, animate);
+ !mUseControlsLayout && colors.supportsDarkText()
+ ? Color.WHITE : Color.BLACK, animate);
View decorView = getWindow().getDecorView();
if (colors.supportsDarkText()) {
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
@@ -2177,7 +2181,7 @@
.start();
ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
- if (mControlsUiController != null) {
+ if (mUseControlsLayout) {
root.setPadding(windowInsets.getStableInsetLeft(),
windowInsets.getStableInsetTop(),
windowInsets.getStableInsetRight(),
@@ -2281,10 +2285,14 @@
public void refreshDialog() {
initializeLayout();
mGlobalActionsLayout.updateList();
+ if (mControlsUiController != null) {
+ mControlsUiController.show(mControlsView);
+ }
}
public void onRotate(int from, int to) {
if (mShowing) {
+ mOnRotateCallback.run();
refreshDialog();
}
}
@@ -2317,4 +2325,10 @@
&& mControlsUiController.getAvailable()
&& !mControlsServiceInfos.isEmpty();
}
-}
+
+ // TODO: Remove legacy layout XML and classes.
+ protected boolean shouldUseControlsLayout() {
+ // always use new controls layout
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 5475812..11b54ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -311,6 +311,7 @@
mTextView.switchIndication(mTransientIndication);
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
mTextView.switchIndication(mAlignmentIndication);
+ mTextView.setTextColor(Utils.getColorError(mContext));
} else if (mPowerPluggedIn) {
String indication = computePowerIndication();
if (animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 31797d1..c9716d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1780,7 +1780,13 @@
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
+ public void onAnimationStart(Animator animation) {
+ notifyExpandingStarted();
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
+ notifyExpandingFinished();
mNotificationStackScroller.resetCheckSnoozeLeavebehind();
mQsExpansionAnimator = null;
if (onFinishRunnable != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index f7d403f..81dc9e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -157,7 +157,7 @@
protected void onExpandingStarted() {
}
- private void notifyExpandingStarted() {
+ protected void notifyExpandingStarted() {
if (!mExpanding) {
mExpanding = true;
onExpandingStarted();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 17cd98f..e65b6fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -197,8 +197,13 @@
TelephonyIcons.THREE_G);
mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EHRPD),
TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS),
+ if (mConfig.show4gFor3g) {
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS),
+ TelephonyIcons.FOUR_G);
+ } else {
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS),
TelephonyIcons.THREE_G);
+ }
mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_TD_SCDMA),
TelephonyIcons.THREE_G);
diff --git a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
index b97f55c..594f0b1 100644
--- a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
+++ b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -18,7 +18,6 @@
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
-import android.content.pm.PackageManager;
import android.testing.AndroidTestingRunner;
import android.text.TextUtils;
import android.util.Log;
@@ -117,12 +116,6 @@
filter.add(s -> s.startsWith("com.android.systemui")
|| s.startsWith("com.android.keyguard"));
-
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- // If it's not automotive target, exclude automotive classes from the test.
- excludeAutomotiveClasses(filter);
- }
-
try {
return scanner.getClassPathEntries(filter);
} catch (IOException e) {
@@ -131,13 +124,6 @@
return Collections.emptyList();
}
- private void excludeAutomotiveClasses(ChainedClassNameFilter filter) {
- // Modifies the passed in filter.
- filter.add(s -> !s.startsWith("com.android.systemui.statusbar.car."));
- filter.add(s -> !s.startsWith("com.android.systemui.qs.car."));
- filter.add(s -> !s.startsWith("com.android.systemui.car."));
- }
-
private String getClsStr() {
return TextUtils.join(",", Arrays.asList(BASE_CLS_WHITELIST)
.stream().map(cls -> cls.getSimpleName()).toArray());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index be5b190..fb40177 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -192,7 +192,8 @@
assertThat(mTextView.getText()).isEqualTo(
mContext.getResources().getString(R.string.dock_alignment_slow_charging));
- assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE);
+ assertThat(mTextView.getCurrentTextColor()).isEqualTo(
+ Utils.getColorError(mContext).getDefaultColor());
}
@Test
@@ -209,7 +210,8 @@
assertThat(mTextView.getText()).isEqualTo(
mContext.getResources().getString(R.string.dock_alignment_not_charging));
- assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE);
+ assertThat(mTextView.getCurrentTextColor()).isEqualTo(
+ Utils.getColorError(mContext).getDefaultColor());
}
@Test
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
index ff83fd1..de2f90e 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
@@ -50,9 +50,6 @@
import com.android.internal.annotations.VisibleForTesting;
-import java.util.Arrays;
-import java.util.List;
-
/**
* A class to display tethering-related notifications.
*
@@ -89,6 +86,9 @@
static final int NO_ICON_ID = 0;
@VisibleForTesting
static final int DOWNSTREAM_NONE = 0;
+ // Refer to TelephonyManager#getSimCarrierId for more details about carrier id.
+ @VisibleForTesting
+ static final int VERIZON_CARRIER_ID = 1839;
private final Context mContext;
private final NotificationManager mNotificationManager;
private final NotificationChannel mChannel;
@@ -114,11 +114,11 @@
@interface NotificationId {}
private static final class MccMncOverrideInfo {
- public final List<String> visitedMccMncs;
+ public final String visitedMccMnc;
public final int homeMcc;
public final int homeMnc;
- MccMncOverrideInfo(List<String> visitedMccMncs, int mcc, int mnc) {
- this.visitedMccMncs = visitedMccMncs;
+ MccMncOverrideInfo(String visitedMccMnc, int mcc, int mnc) {
+ this.visitedMccMnc = visitedMccMnc;
this.homeMcc = mcc;
this.homeMnc = mnc;
}
@@ -127,9 +127,7 @@
private static final SparseArray<MccMncOverrideInfo> sCarrierIdToMccMnc = new SparseArray<>();
static {
- // VZW
- sCarrierIdToMccMnc.put(
- 1839, new MccMncOverrideInfo(Arrays.asList(new String[] {"20404"}), 311, 480));
+ sCarrierIdToMccMnc.put(VERIZON_CARRIER_ID, new MccMncOverrideInfo("20404", 311, 480));
}
public TetheringNotificationUpdater(@NonNull final Context context,
@@ -200,7 +198,7 @@
final int carrierId = tm.getSimCarrierId();
final String mccmnc = tm.getSimOperator();
final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId);
- if (overrideInfo != null && overrideInfo.visitedMccMncs.contains(mccmnc)) {
+ if (overrideInfo != null && overrideInfo.visitedMccMnc.equals(mccmnc)) {
// Re-configure MCC/MNC value to specific carrier to get right resources.
final Configuration config = res.getConfiguration();
config.mcc = overrideInfo.homeMcc;
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
index 5f88588..294bf1b 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -39,6 +39,7 @@
import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID
import com.android.testutils.waitForIdle
import org.junit.After
import org.junit.Assert.assertEquals
@@ -417,7 +418,7 @@
assertEquals(config.mcc, res.configuration.mcc)
assertEquals(config.mnc, res.configuration.mnc)
- doReturn(1839).`when`(telephonyManager).getSimCarrierId()
+ doReturn(VERIZON_CARRIER_ID).`when`(telephonyManager).getSimCarrierId()
res = notificationUpdater.getResourcesForSubId(context, subId)
assertEquals(config.mcc, res.configuration.mcc)
assertEquals(config.mnc, res.configuration.mnc)
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 34d2b73..7750e32 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -256,6 +256,10 @@
// Package: android
NOTE_ADB_WIFI_ACTIVE = 62;
+ // Notify user there was a non-framework gnss location access during an emergency
+ // Package: android
+ NOTE_GNSS_NFW_LOCATION_ACCESS = 63;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index fe33fae9..ce65110 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1741,8 +1741,9 @@
final long nowElapsed = mInjector.getElapsedRealtime();
final long nominalTrigger = convertToElapsed(triggerAtTime, type);
- // Try to prevent spamming by making sure we aren't firing alarms in the immediate future
- final long minTrigger = nowElapsed + mConstants.MIN_FUTURITY;
+ // Try to prevent spamming by making sure apps aren't firing alarms in the immediate future
+ final long minTrigger = nowElapsed
+ + (UserHandle.isCore(callingUid) ? 0L : mConstants.MIN_FUTURITY);
final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger;
final long maxElapsed;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1a58d60..f1ea5d0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2093,6 +2093,20 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
+ private void enforceNetworkFactoryOrSettingsPermission() {
+ enforceAnyPermissionOf(
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_FACTORY,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
+ private void enforceNetworkFactoryOrTestNetworksPermission() {
+ enforceAnyPermissionOf(
+ android.Manifest.permission.MANAGE_TEST_NETWORKS,
+ android.Manifest.permission.NETWORK_FACTORY,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
private boolean checkSettingsPermission() {
return checkAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
@@ -2722,7 +2736,9 @@
break;
}
case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
- handleUpdateLinkProperties(nai, (LinkProperties) msg.obj);
+ LinkProperties newLp = (LinkProperties) msg.obj;
+ processLinkPropertiesFromAgent(nai, newLp);
+ handleUpdateLinkProperties(nai, newLp);
break;
}
case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
@@ -5681,7 +5697,7 @@
@Override
public int registerNetworkProvider(Messenger messenger, String name) {
- enforceNetworkFactoryPermission();
+ enforceNetworkFactoryOrSettingsPermission();
NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
null /* asyncChannel */, nextNetworkProviderId(),
() -> unregisterNetworkProvider(messenger));
@@ -5691,7 +5707,7 @@
@Override
public void unregisterNetworkProvider(Messenger messenger) {
- enforceNetworkFactoryPermission();
+ enforceNetworkFactoryOrSettingsPermission();
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
}
@@ -5711,7 +5727,11 @@
@Override
public void declareNetworkRequestUnfulfillable(NetworkRequest request) {
- enforceNetworkFactoryPermission();
+ if (request.hasTransport(TRANSPORT_TEST)) {
+ enforceNetworkFactoryOrTestNetworksPermission();
+ } else {
+ enforceNetworkFactoryPermission();
+ }
mHandler.post(() -> handleReleaseNetworkRequest(request, Binder.getCallingUid(), true));
}
@@ -5817,7 +5837,7 @@
}
LinkProperties lp = new LinkProperties(linkProperties);
- lp.ensureDirectlyConnectedRoutes();
+
// TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
// satisfies mDefaultRequest.
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
@@ -5825,8 +5845,11 @@
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
this, mNetd, mDnsResolver, mNMS, providerId);
- // Make sure the network capabilities reflect what the agent info says.
+
+ // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
+ processLinkPropertiesFromAgent(nai, nai.linkProperties);
+
final String extraInfo = networkInfo.getExtraInfo();
final String name = TextUtils.isEmpty(extraInfo)
? nai.networkCapabilities.getSsid() : extraInfo;
@@ -5864,6 +5887,10 @@
updateUids(nai, null, nai.networkCapabilities);
}
+ private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) {
+ lp.ensureDirectlyConnectedRoutes();
+ }
+
private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
@NonNull LinkProperties oldLp) {
int netId = networkAgent.network.netId;
@@ -6391,13 +6418,13 @@
// Ignore updates for disconnected networks
return;
}
- // newLp is already a defensive copy.
- newLp.ensureDirectlyConnectedRoutes();
if (VDBG || DDBG) {
log("Update of LinkProperties for " + nai.toShortString()
+ "; created=" + nai.created
+ "; everConnected=" + nai.everConnected);
}
+ // TODO: eliminate this defensive copy after confirming that updateLinkProperties does not
+ // modify its oldLp parameter.
updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 741cb5b..e6b2d26 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -81,6 +81,9 @@
RUNNING, // start() called, and the stacked iface is known to be up.
}
+ /** NAT64 prefix currently in use. Only valid in STARTING or RUNNING states. */
+ private IpPrefix mNat64PrefixInUse;
+ /** NAT64 prefix (if any) discovered from DNS via RFC 7050. */
private IpPrefix mNat64PrefixFromDns;
private String mBaseIface;
private String mIface;
@@ -178,9 +181,10 @@
return;
}
+ mNat64PrefixInUse = getNat64Prefix();
String addrStr = null;
try {
- addrStr = mNetd.clatdStart(baseIface, getNat64Prefix().toString());
+ addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
}
@@ -211,12 +215,13 @@
} catch (RemoteException | IllegalStateException e) {
Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
}
+ mNat64PrefixInUse = null;
mIface = null;
mBaseIface = null;
if (requiresClat(mNetwork)) {
mState = State.DISCOVERING;
} else {
- stopPrefixDiscovery();
+ stopPrefixDiscovery(); // Enters IDLE state.
}
}
@@ -274,19 +279,32 @@
private void startPrefixDiscovery() {
try {
mDnsResolver.startPrefix64Discovery(getNetId());
- mState = State.DISCOVERING;
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
}
+ mState = State.DISCOVERING;
}
private void stopPrefixDiscovery() {
try {
mDnsResolver.stopPrefix64Discovery(getNetId());
- mState = State.IDLE;
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
}
+ mState = State.IDLE;
+ }
+
+ private void maybeHandleNat64PrefixChange() {
+ final IpPrefix newPrefix = getNat64Prefix();
+ if (!Objects.equals(mNat64PrefixInUse, newPrefix)) {
+ Slog.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to "
+ + newPrefix);
+ stop();
+ // It's safe to call update here, even though this method is called from update, because
+ // stop() is guaranteed to have moved out of STARTING and RUNNING, which are the only
+ // states in which this method can be called.
+ update();
+ }
}
/**
@@ -325,11 +343,11 @@
// Stop clatd and go back into DISCOVERING or idle.
if (!shouldStartClat(mNetwork)) {
stop();
+ break;
}
+ // Only necessary while clat is actually started.
+ maybeHandleNat64PrefixChange();
break;
- // TODO: support the NAT64 prefix changing after it's been discovered. There is
- // no need to support this at the moment because it cannot happen without
- // changes to the Dns64Configuration code in netd.
}
}
@@ -347,6 +365,8 @@
* has no idea that 464xlat is running on top of it.
*/
public void fixupLinkProperties(@NonNull LinkProperties oldLp, @NonNull LinkProperties lp) {
+ // This must be done even if clatd is not running, because otherwise shouldStartClat would
+ // never return true.
lp.setNat64Prefix(getNat64Prefix());
if (!isRunning()) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index 3fb713b..e17cca4 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -17,8 +17,9 @@
package com.android.server.location.gnss;
import android.content.Context;
-import android.database.Cursor;
import android.net.ConnectivityManager;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
@@ -26,25 +27,26 @@
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
-import android.provider.Telephony.Carriers;
-import android.telephony.ServiceState;
-import android.telephony.TelephonyManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.PreciseCallState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
-import android.telephony.PreciseCallState;
-import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.location.GpsNetInitiatedHandler;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
-import java.util.Map;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
/**
* Handles network connection requests and network state change updates for AGPS data download.
@@ -385,10 +387,10 @@
private ConnectivityManager.NetworkCallback createSuplConnectivityCallback() {
return new ConnectivityManager.NetworkCallback() {
@Override
- public void onAvailable(Network network) {
+ public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
if (DEBUG) Log.d(TAG, "SUPL network connection available.");
// Specific to a change to a SUPL enabled network becoming ready
- handleSuplConnectionAvailable(network);
+ handleSuplConnectionAvailable(network, linkProperties);
}
@Override
@@ -496,7 +498,7 @@
return networkAttributes;
}
- private void handleSuplConnectionAvailable(Network network) {
+ private void handleSuplConnectionAvailable(Network network, LinkProperties linkProperties) {
// TODO: The synchronous method ConnectivityManager.getNetworkInfo() should not be called
// inside the asynchronous ConnectivityManager.NetworkCallback methods.
NetworkInfo info = mConnMgr.getNetworkInfo(network);
@@ -528,7 +530,7 @@
setRouting();
}
- int apnIpType = getApnIpType(apn);
+ int apnIpType = getLinkIpType(linkProperties);
if (DEBUG) {
String message = String.format(
"native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s",
@@ -704,74 +706,32 @@
}
}
- private int getApnIpType(String apn) {
+ private int getLinkIpType(LinkProperties linkProperties) {
ensureInHandlerThread();
- if (apn == null) {
- return APN_INVALID;
- }
- TelephonyManager phone = (TelephonyManager)
- mContext.getSystemService(Context.TELEPHONY_SERVICE);
- // During an emergency call with an active sub id, get the Telephony Manager specific
- // to the active sub to get the correct value from getServiceState and getNetworkType
- if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
- TelephonyManager subIdTelManager =
- phone.createForSubscriptionId(mActiveSubId);
- if (subIdTelManager != null) {
- phone = subIdTelManager;
+ boolean isIPv4 = false;
+ boolean isIPv6 = false;
+
+ List<LinkAddress> linkAddresses = linkProperties.getLinkAddresses();
+ for (LinkAddress linkAddress : linkAddresses) {
+ InetAddress inetAddress = linkAddress.getAddress();
+ if (inetAddress instanceof Inet4Address) {
+ isIPv4 = true;
+ } else if (inetAddress instanceof Inet6Address) {
+ isIPv6 = true;
}
- }
- ServiceState serviceState = phone.getServiceState();
- String projection = null;
- String selection = null;
-
- // Carrier configuration may override framework roaming state, we need to use the actual
- // modem roaming state instead of the framework roaming state.
- if (serviceState != null && serviceState.getDataRoamingFromRegistration()) {
- projection = Carriers.ROAMING_PROTOCOL;
- } else {
- projection = Carriers.PROTOCOL;
- }
- // No SIM case for emergency
- if (TelephonyManager.NETWORK_TYPE_UNKNOWN == phone.getNetworkType()
- && AGPS_TYPE_EIMS == mAGpsType) {
- selection = String.format(
- "type like '%%emergency%%' and apn = '%s' and carrier_enabled = 1", apn);
- } else {
- selection = String.format("current = 1 and apn = '%s' and carrier_enabled = 1", apn);
- }
- try (Cursor cursor = mContext.getContentResolver().query(
- Carriers.CONTENT_URI,
- new String[]{projection},
- selection,
- null,
- Carriers.DEFAULT_SORT_ORDER)) {
- if (null != cursor && cursor.moveToFirst()) {
- return translateToApnIpType(cursor.getString(0), apn);
- } else {
- Log.e(TAG, "No entry found in query for APN: " + apn);
- }
- } catch (Exception e) {
- Log.e(TAG, "Error encountered on APN query for: " + apn, e);
+ if (DEBUG) Log.d(TAG, "LinkAddress : " + inetAddress.toString());
}
- return APN_IPV4V6;
- }
-
- private int translateToApnIpType(String ipProtocol, String apn) {
- if ("IP".equals(ipProtocol)) {
- return APN_IPV4;
- }
- if ("IPV6".equals(ipProtocol)) {
- return APN_IPV6;
- }
- if ("IPV4V6".equals(ipProtocol)) {
+ if (isIPv4 && isIPv6) {
return APN_IPV4V6;
}
-
- // we hit the default case so the ipProtocol is not recognized
- String message = String.format("Unknown IP Protocol: %s, for APN: %s", ipProtocol, apn);
- Log.e(TAG, message);
- return APN_IPV4V6;
+ if (isIPv4) {
+ return APN_IPV4;
+ }
+ if (isIPv6) {
+ return APN_IPV6;
+ }
+ return APN_INVALID;
}
// AGPS support
diff --git a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
index 06fa0ea..75fd7dc 100644
--- a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
@@ -21,13 +21,13 @@
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.hardware.gnss.visibility_control.V1_0.IGnssVisibilityControlCallback;
import android.location.LocationManager;
import android.os.Handler;
import android.os.Looper;
@@ -39,12 +39,14 @@
import com.android.internal.R;
import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.FrameworkStatsLog;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Handles GNSS non-framework location access user visibility and control.
@@ -283,21 +285,22 @@
// Represents NfwNotification structure in IGnssVisibilityControlCallback.hal
private static class NfwNotification {
- // These must match with NfwResponseType enum in IGnssVisibilityControlCallback.hal.
- private static final byte NFW_RESPONSE_TYPE_REJECTED = 0;
- private static final byte NFW_RESPONSE_TYPE_ACCEPTED_NO_LOCATION_PROVIDED = 1;
- private static final byte NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED = 2;
- private final String mProxyAppPackageName;
- private final byte mProtocolStack;
- private final String mOtherProtocolStackName;
- private final byte mRequestor;
- private final String mRequestorId;
- private final byte mResponseType;
- private final boolean mInEmergencyMode;
- private final boolean mIsCachedLocation;
+ // These must match with NfwResponseType enum in IGnssVisibilityControlCallback.hal
+ static final byte NFW_RESPONSE_TYPE_REJECTED = 0;
+ static final byte NFW_RESPONSE_TYPE_ACCEPTED_NO_LOCATION_PROVIDED = 1;
+ static final byte NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED = 2;
- private NfwNotification(String proxyAppPackageName, byte protocolStack,
+ final String mProxyAppPackageName;
+ final byte mProtocolStack;
+ final String mOtherProtocolStackName;
+ final byte mRequestor;
+ final String mRequestorId;
+ final byte mResponseType;
+ final boolean mInEmergencyMode;
+ final boolean mIsCachedLocation;
+
+ NfwNotification(String proxyAppPackageName, byte protocolStack,
String otherProtocolStackName, byte requestor, String requestorId,
byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
mProxyAppPackageName = proxyAppPackageName;
@@ -610,43 +613,38 @@
logEvent(nfwNotification, isPermissionMismatched);
if (nfwNotification.isLocationProvided()) {
- postEmergencyLocationUserNotification(nfwNotification);
+ displayNfwNotification(nfwNotification);
}
}
- private void postEmergencyLocationUserNotification(NfwNotification nfwNotification) {
- // Emulate deprecated IGnssNi.hal user notification of emergency NI requests.
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
- if (notificationManager == null) {
- Log.w(TAG, "Could not notify user of emergency location request. Notification: "
- + nfwNotification);
- return;
+ private void displayNfwNotification(NfwNotification nfwNotification) {
+ NotificationManager notificationManager = Objects.requireNonNull(
+ mContext.getSystemService(NotificationManager.class));
+
+ String title = mContext.getString(R.string.gnss_nfw_notification_title);
+ String message;
+ if (nfwNotification.mRequestor == IGnssVisibilityControlCallback.NfwRequestor.CARRIER) {
+ message = mContext.getString(R.string.gnss_nfw_notification_message_carrier);
+ } else {
+ message = mContext.getString(R.string.gnss_nfw_notification_message_oem);
}
- notificationManager.notifyAsUser(/* tag= */ null, /* notificationId= */ 0,
- createEmergencyLocationUserNotification(mContext), UserHandle.ALL);
- }
-
- private static Notification createEmergencyLocationUserNotification(Context context) {
- // NOTE: Do not reuse the returned notification object as it will not reflect
- // changes to notification text when the system language is changed.
- final String firstLineText = context.getString(R.string.gpsNotifTitle);
- final String secondLineText = context.getString(R.string.global_action_emergency);
- final String accessibilityServicesText = firstLineText + " (" + secondLineText + ")";
- return new Notification.Builder(context, SystemNotificationChannels.NETWORK_ALERTS)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_gps_on)
- .setWhen(0)
- .setOngoing(false)
+ Notification.Builder builder = new Notification.Builder(mContext,
+ SystemNotificationChannels.NETWORK_ALERTS)
+ .setSmallIcon(R.drawable.stat_sys_gps_on)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setVisibility(Notification.VISIBILITY_SECRET)
+ .setContentTitle(title)
+ .setTicker(title)
+ .setContentText(message)
+ .setStyle(new Notification.BigTextStyle().bigText(message))
.setAutoCancel(true)
- .setColor(context.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setDefaults(0)
- .setTicker(accessibilityServicesText)
- .setContentTitle(firstLineText)
- .setContentText(secondLineText)
- .setContentIntent(PendingIntent.getBroadcast(context, 0, new Intent(), 0))
- .build();
+ .setColor(mContext.getColor(R.color.system_notification_accent_color))
+ .setWhen(System.currentTimeMillis())
+ .setShowWhen(true)
+ .setDefaults(0);
+
+ notificationManager.notify(SystemMessage.NOTE_GNSS_NFW_LOCATION_ACCESS, builder.build());
}
private void logEvent(NfwNotification notification, boolean isPermissionMismatched) {
diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
index e0efd8a..0b96978 100644
--- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java
+++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
@@ -16,24 +16,64 @@
package com.android.server.media;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.session.ISessionManager;
import android.media.session.MediaSession;
import android.os.Binder;
import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
/**
* Provides a way to customize behavior for media key events.
- *
+ * <p>
+ * In order to override the implementation of the single/double/triple click or long press,
+ * {@link #setOverriddenKeyEvents(int, int)} should be called for each key code with the
+ * overridden {@link KeyEventType} bit value set, and the corresponding method,
+ * {@link #onSingleClick(KeyEvent)}, {@link #onDoubleClick(KeyEvent)},
+ * {@link #onTripleClick(KeyEvent)}, {@link #onLongPress(KeyEvent)} should be implemented.
+ * <p>
* Note: When instantiating this class, {@link MediaSessionService} will only use the constructor
* without any parameters.
*/
+// TODO: Change API names from using "click" to "tap"
+// TODO: Move this class to apex/media/
public abstract class MediaKeyDispatcher {
+ @IntDef(flag = true, value = {
+ KEY_EVENT_SINGLE_CLICK,
+ KEY_EVENT_DOUBLE_CLICK,
+ KEY_EVENT_TRIPLE_CLICK,
+ KEY_EVENT_LONG_PRESS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface KeyEventType {}
+ static final int KEY_EVENT_SINGLE_CLICK = 1 << 0;
+ static final int KEY_EVENT_DOUBLE_CLICK = 1 << 1;
+ static final int KEY_EVENT_TRIPLE_CLICK = 1 << 2;
+ static final int KEY_EVENT_LONG_PRESS = 1 << 3;
+
+ private Map<Integer, Integer> mOverriddenKeyEvents;
+
public MediaKeyDispatcher() {
// Constructor used for reflection
+ mOverriddenKeyEvents = new HashMap<>();
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PLAY, 0);
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PAUSE, 0);
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0);
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MUTE, 0);
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_HEADSETHOOK, 0);
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_STOP, 0);
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_NEXT, 0);
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS, 0);
}
+ // TODO: Move this method into SessionPolicyProvider.java for better readability.
/**
* Implement this to customize the logic for which MediaSession should consume which key event.
*
@@ -49,4 +89,137 @@
boolean asSystemService) {
return null;
}
+
+ /**
+ * Gets the map of key code -> {@link KeyEventType} that have been overridden.
+ * <p>
+ * The list of valid key codes are the following:
+ * <ul>
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY}
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_PAUSE}
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE}
+ * <li> {@link KeyEvent#KEYCODE_MUTE}
+ * <li> {@link KeyEvent#KEYCODE_HEADSETHOOK}
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_STOP}
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_NEXT}
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS}
+ * </ul>
+ * @see {@link KeyEvent#isMediaSessionKey(int)}
+ */
+ @KeyEventType Map<Integer, Integer> getOverriddenKeyEvents() {
+ return mOverriddenKeyEvents;
+ }
+
+ static boolean isSingleClickOverridden(@KeyEventType int overriddenKeyEvents) {
+ return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_SINGLE_CLICK) != 0;
+ }
+
+ static boolean isDoubleClickOverridden(@KeyEventType int overriddenKeyEvents) {
+ return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_DOUBLE_CLICK) != 0;
+ }
+
+ static boolean isTripleClickOverridden(@KeyEventType int overriddenKeyEvents) {
+ return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_TRIPLE_CLICK) != 0;
+ }
+
+ static boolean isLongPressOverridden(@KeyEventType int overriddenKeyEvents) {
+ return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_LONG_PRESS) != 0;
+ }
+
+ /**
+ * Sets the value of the given key event type flagged with overridden {@link KeyEventType} to
+ * the given key code. If called multiple times for the same key code, will be overwritten to
+ * the most recently called {@link KeyEventType} value.
+ * <p>
+ * The list of valid key codes are the following:
+ * <ul>
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY}
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_PAUSE}
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE}
+ * <li> {@link KeyEvent#KEYCODE_MUTE}
+ * <li> {@link KeyEvent#KEYCODE_HEADSETHOOK}
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_STOP}
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_NEXT}
+ * <li> {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS}
+ * </ul>
+ * @see {@link KeyEvent#isMediaSessionKey(int)}
+ * @param keyCode
+ */
+ void setOverriddenKeyEvents(int keyCode, @KeyEventType int keyEventType) {
+ mOverriddenKeyEvents.put(keyCode, keyEventType);
+ }
+
+ /**
+ * Customized implementation for single click event. Will be run if
+ * {@link #KEY_EVENT_SINGLE_CLICK} flag is on for the corresponding key code from
+ * {@link #getOverriddenKeyEvents()}.
+ *
+ * It is considered a single click if only one {@link KeyEvent} with the same
+ * {@link KeyEvent#getKeyCode()} is dispatched within
+ * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds. Change the
+ * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval.
+ *
+ * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent.
+ *
+ * @param keyEvent
+ */
+ void onSingleClick(KeyEvent keyEvent) {
+ }
+
+ /**
+ * Customized implementation for double click event. Will be run if
+ * {@link #KEY_EVENT_DOUBLE_CLICK} flag is on for the corresponding key code from
+ * {@link #getOverriddenKeyEvents()}.
+ *
+ * It is considered a double click if two {@link KeyEvent}s with the same
+ * {@link KeyEvent#getKeyCode()} are dispatched within
+ * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds of each other. Change the
+ * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval.
+ *
+ * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent.
+ *
+ * @param keyEvent
+ */
+ void onDoubleClick(KeyEvent keyEvent) {
+ }
+
+ /**
+ * Customized implementation for triple click event. Will be run if
+ * {@link #KEY_EVENT_TRIPLE_CLICK} flag is on for the corresponding key code from
+ * {@link #getOverriddenKeyEvents()}.
+ *
+ * It is considered a triple click if three {@link KeyEvent}s with the same
+ * {@link KeyEvent#getKeyCode()} are dispatched within
+ * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds of each other. Change the
+ * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval.
+ *
+ * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent.
+ *
+ * @param keyEvent
+ */
+ void onTripleClick(KeyEvent keyEvent) {
+ }
+
+ /**
+ * Customized implementation for long press event. Will be run if
+ * {@link #KEY_EVENT_LONG_PRESS} flag is on for the corresponding key code from
+ * {@link #getOverriddenKeyEvents()}.
+ *
+ * It is considered a long press if an {@link KeyEvent#ACTION_DOWN} key event is followed by
+ * another {@link KeyEvent#ACTION_DOWN} key event with {@link KeyEvent#FLAG_LONG_PRESS}
+ * enabled, and an {@link KeyEvent#getRepeatCount()} that is equal to 1.
+ *
+ * Note: This will be called for the following key events:
+ * <ul>
+ * <li>A {@link KeyEvent#ACTION_DOWN} KeyEvent with {@link KeyEvent#FLAG_LONG_PRESS} and
+ * {@link KeyEvent#getRepeatCount()} equal to 1</li>
+ * <li>Multiple {@link KeyEvent#ACTION_DOWN} KeyEvents with increasing
+ * {@link KeyEvent#getRepeatCount()}</li>
+ * <li>A {@link KeyEvent#ACTION_UP} KeyEvent</li>
+ * </ul>
+ *
+ * @param keyEvent
+ */
+ void onLongPress(KeyEvent keyEvent) {
+ }
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 5757b1a..e5867e7 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -86,10 +86,10 @@
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* System implementation of MediaSessionManager
@@ -103,12 +103,14 @@
private static final int WAKELOCK_TIMEOUT = 5000;
private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
private static final int SESSION_CREATION_LIMIT_PER_UID = 100;
+ private static final int LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout()
+ + /* Buffer for delayed delivery of key event */ 50;
+ private static final int MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout();
private final Context mContext;
private final SessionManagerImpl mSessionManagerImpl;
private final MessageHandler mHandler = new MessageHandler();
private final PowerManager.WakeLock mMediaEventWakeLock;
- private final int mLongPressTimeout;
private final INotificationManager mNotificationManager;
private final Object mLock = new Object();
// Keeps the full user id for each user.
@@ -142,8 +144,7 @@
private SessionPolicyProvider mCustomSessionPolicyProvider;
private MediaKeyDispatcher mCustomMediaKeyDispatcher;
- private Method mGetSessionForKeyEventMethod;
- private Method mGetSessionPoliciesMethod;
+ private Map<Integer, Integer> mOverriddenKeyEventsMap;
public MediaSessionService(Context context) {
super(context);
@@ -151,7 +152,6 @@
mSessionManagerImpl = new SessionManagerImpl();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
- mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
mNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
}
@@ -184,9 +184,10 @@
mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
+ updateUser();
+
instantiateCustomProvider(null);
instantiateCustomDispatcher(null);
- updateUser();
}
private boolean isGlobalPriorityActiveLocked() {
@@ -570,13 +571,9 @@
String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) {
synchronized (mLock) {
int policies = 0;
- if (mCustomSessionPolicyProvider != null && mGetSessionPoliciesMethod != null) {
- try {
- policies = (int) mGetSessionPoliciesMethod.invoke(
- mCustomSessionPolicyProvider, callerUid, callerPackageName);
- } catch (InvocationTargetException | IllegalAccessException e) {
- Log.w(TAG, "Encountered problem while using reflection", e);
- }
+ if (mCustomSessionPolicyProvider != null) {
+ policies = mCustomSessionPolicyProvider.getSessionPoliciesForApplication(
+ callerUid, callerPackageName);
}
FullUserRecord user = getFullUserRecordLocked(userId);
@@ -762,44 +759,46 @@
}
private void instantiateCustomDispatcher(String nameFromTesting) {
- mCustomMediaKeyDispatcher = null;
- mGetSessionForKeyEventMethod = null;
+ synchronized (mLock) {
+ mCustomMediaKeyDispatcher = null;
+ mOverriddenKeyEventsMap = null;
- String customDispatcherClassName = (nameFromTesting == null)
- ? mContext.getResources().getString(R.string.config_customMediaKeyDispatcher)
- : nameFromTesting;
- try {
- if (!TextUtils.isEmpty(customDispatcherClassName)) {
- Class customDispatcherClass = Class.forName(customDispatcherClassName);
- Constructor constructor = customDispatcherClass.getDeclaredConstructor();
- mCustomMediaKeyDispatcher = (MediaKeyDispatcher) constructor.newInstance();
- mGetSessionForKeyEventMethod = customDispatcherClass.getDeclaredMethod(
- "getSessionForKeyEvent", KeyEvent.class, int.class, boolean.class);
+ String customDispatcherClassName = (nameFromTesting == null)
+ ? mContext.getResources().getString(R.string.config_customMediaKeyDispatcher)
+ : nameFromTesting;
+ try {
+ if (!TextUtils.isEmpty(customDispatcherClassName)) {
+ Class customDispatcherClass = Class.forName(customDispatcherClassName);
+ Constructor constructor = customDispatcherClass.getDeclaredConstructor();
+ mCustomMediaKeyDispatcher = (MediaKeyDispatcher) constructor.newInstance();
+ mOverriddenKeyEventsMap = mCustomMediaKeyDispatcher.getOverriddenKeyEvents();
+ }
+ } catch (ClassNotFoundException | InstantiationException | InvocationTargetException
+ | IllegalAccessException | NoSuchMethodException e) {
+ mCustomMediaKeyDispatcher = null;
+ Log.w(TAG, "Encountered problem while using reflection", e);
}
- } catch (ClassNotFoundException | InstantiationException | InvocationTargetException
- | IllegalAccessException | NoSuchMethodException e) {
- Log.w(TAG, "Encountered problem while using reflection", e);
}
}
private void instantiateCustomProvider(String nameFromTesting) {
- mCustomSessionPolicyProvider = null;
- mGetSessionPoliciesMethod = null;
+ synchronized (mLock) {
+ mCustomSessionPolicyProvider = null;
- String customProviderClassName = (nameFromTesting == null)
- ? mContext.getResources().getString(R.string.config_customSessionPolicyProvider)
- : nameFromTesting;
- try {
- if (!TextUtils.isEmpty(customProviderClassName)) {
- Class customProviderClass = Class.forName(customProviderClassName);
- Constructor constructor = customProviderClass.getDeclaredConstructor();
- mCustomSessionPolicyProvider = (SessionPolicyProvider) constructor.newInstance();
- mGetSessionPoliciesMethod = customProviderClass.getDeclaredMethod(
- "getSessionPoliciesForApplication", int.class, String.class);
+ String customProviderClassName = (nameFromTesting == null)
+ ? mContext.getResources().getString(R.string.config_customSessionPolicyProvider)
+ : nameFromTesting;
+ try {
+ if (!TextUtils.isEmpty(customProviderClassName)) {
+ Class customProviderClass = Class.forName(customProviderClassName);
+ Constructor constructor = customProviderClass.getDeclaredConstructor();
+ mCustomSessionPolicyProvider =
+ (SessionPolicyProvider) constructor.newInstance();
+ }
+ } catch (ClassNotFoundException | InstantiationException | InvocationTargetException
+ | IllegalAccessException | NoSuchMethodException e) {
+ Log.w(TAG, "Encountered problem while using reflection", e);
}
- } catch (ClassNotFoundException | InstantiationException | InvocationTargetException
- | IllegalAccessException | NoSuchMethodException e) {
- Log.w(TAG, "Encountered problem while using reflection", e);
}
}
@@ -1098,8 +1097,9 @@
"android.media.AudioService.WAKELOCK_ACQUIRED";
private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
- private boolean mVoiceButtonDown = false;
- private boolean mVoiceButtonHandled = false;
+ private KeyEvent mPendingFirstDownKeyEvent = null;
+ private boolean mIsLongPressing = false;
+ private Runnable mLongPressTimeoutRunnable = null;
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
@@ -1362,12 +1362,12 @@
}
}
}
- if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
- handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
- needWakeLock);
- } else {
+ if (isGlobalPriorityActive) {
dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
keyEvent, needWakeLock);
+ } else {
+ handleKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
+ needWakeLock);
}
}
} finally {
@@ -1641,7 +1641,7 @@
}
/**
- * Dispaches volume key events. This is called when the foreground activity didn't handled
+ * Dispatches volume key events. This is called when the foreground activity didn't handle
* the incoming volume key event.
* <p>
* Handles the dispatching of the volume button events to one of the
@@ -1662,7 +1662,7 @@
* {@link KeyEvent#KEYCODE_VOLUME_DOWN},
* or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
* @param stream stream type to adjust volume.
- * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
+ * @param musicOnly true if both UI and haptic feedback aren't needed when adjusting volume.
* @see #dispatchVolumeKeyEventToSessionAsSystemService
*/
@Override
@@ -1707,7 +1707,7 @@
mHandler.obtainMessage(
MessageHandler.MSG_VOLUME_INITIAL_DOWN,
mCurrentFullUserRecord.mFullUserId, 0),
- mLongPressTimeout);
+ LONG_PRESS_TIMEOUT);
}
if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
@@ -2112,31 +2112,147 @@
}
}
- private void handleVoiceKeyEventLocked(String packageName, int pid, int uid,
+ // A long press is determined by:
+ // 1) A KeyEvent with KeyEvent.ACTION_DOWN and repeat count of 0, followed by
+ // 2) A KeyEvent with KeyEvent.ACTION_DOWN and repeat count of 1 and FLAG_LONG_PRESS within
+ // ViewConfiguration.getLongPressTimeout().
+ // TODO: Add description about what a click is determined by.
+ private void handleKeyEventLocked(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
- int action = keyEvent.getAction();
- boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
- if (action == KeyEvent.ACTION_DOWN) {
- if (keyEvent.getRepeatCount() == 0) {
- mVoiceButtonDown = true;
- mVoiceButtonHandled = false;
- } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
- mVoiceButtonHandled = true;
- startVoiceInput(needWakeLock);
+ if (keyEvent.isCanceled()) {
+ return;
+ }
+
+ int overriddenKeyEvents = (mCustomMediaKeyDispatcher == null) ? 0
+ : mCustomMediaKeyDispatcher.getOverriddenKeyEvents().get(keyEvent.getKeyCode());
+ cancelPendingIfNeeded(keyEvent);
+ if (!needPending(keyEvent, overriddenKeyEvents)) {
+ dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
+ needWakeLock);
+ return;
+ }
+
+ if (isFirstDownKeyEvent(keyEvent)) {
+ mPendingFirstDownKeyEvent = keyEvent;
+ mIsLongPressing = false;
+ return;
+ }
+
+ if (isFirstLongPressKeyEvent(keyEvent)) {
+ mIsLongPressing = true;
+ }
+ if (mIsLongPressing) {
+ handleLongPressLocked(keyEvent, needWakeLock, overriddenKeyEvents);
+ } else if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
+ mPendingFirstDownKeyEvent = null;
+ // TODO: Replace this with code to determine whether
+ // single/double/triple click and run custom implementations,
+ // if they exist.
+ dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService,
+ keyEvent, needWakeLock);
+ }
+ }
+
+ private void cancelPendingIfNeeded(KeyEvent keyEvent) {
+ if (mPendingFirstDownKeyEvent == null) {
+ return;
+ }
+ if (isFirstDownKeyEvent(keyEvent)) {
+ if (mLongPressTimeoutRunnable != null) {
+ mHandler.removeCallbacks(mLongPressTimeoutRunnable);
+ mLongPressTimeoutRunnable.run();
+ } else {
+ resetLongPressTracking();
}
- } else if (action == KeyEvent.ACTION_UP) {
- if (mVoiceButtonDown) {
- mVoiceButtonDown = false;
- if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
- // Resend the down then send this event through
- KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
- dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
- downEvent, needWakeLock);
- dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
- keyEvent, needWakeLock);
- }
+ return;
+ }
+ if (mPendingFirstDownKeyEvent.getDownTime() == keyEvent.getDownTime()
+ && mPendingFirstDownKeyEvent.getKeyCode() == keyEvent.getKeyCode()
+ && keyEvent.getAction() == KeyEvent.ACTION_DOWN
+ && keyEvent.getRepeatCount() > 1 && !mIsLongPressing) {
+ resetLongPressTracking();
+ }
+ }
+
+ private boolean needPending(KeyEvent keyEvent, int overriddenKeyEvents) {
+ if (!isFirstDownKeyEvent(keyEvent)) {
+ if (mPendingFirstDownKeyEvent == null) {
+ return false;
+ } else if (mPendingFirstDownKeyEvent.getDownTime() != keyEvent.getDownTime()
+ || mPendingFirstDownKeyEvent.getKeyCode() != keyEvent.getKeyCode()) {
+ return false;
}
}
+ if (overriddenKeyEvents == 0 && !isVoiceKey(keyEvent.getKeyCode())) {
+ return false;
+ }
+ return true;
+ }
+
+ private void handleLongPressLocked(KeyEvent keyEvent, boolean needWakeLock,
+ int overriddenKeyEvents) {
+ if (mCustomMediaKeyDispatcher != null
+ && mCustomMediaKeyDispatcher.isLongPressOverridden(overriddenKeyEvents)) {
+ mCustomMediaKeyDispatcher.onLongPress(keyEvent);
+
+ if (mLongPressTimeoutRunnable != null) {
+ mHandler.removeCallbacks(mLongPressTimeoutRunnable);
+ }
+ if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+ if (mLongPressTimeoutRunnable == null) {
+ mLongPressTimeoutRunnable = createLongPressTimeoutRunnable(keyEvent);
+ }
+ mHandler.postDelayed(mLongPressTimeoutRunnable, LONG_PRESS_TIMEOUT);
+ } else {
+ resetLongPressTracking();
+ }
+ } else if (isFirstLongPressKeyEvent(keyEvent) && isVoiceKey(keyEvent.getKeyCode())) {
+ // Default implementation
+ startVoiceInput(needWakeLock);
+ resetLongPressTracking();
+ }
+ }
+
+ private Runnable createLongPressTimeoutRunnable(KeyEvent keyEvent) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ if (mCustomMediaKeyDispatcher != null) {
+ mCustomMediaKeyDispatcher.onLongPress(createCanceledKeyEvent(keyEvent));
+ }
+ resetLongPressTracking();
+ }
+ };
+ }
+
+ private void resetLongPressTracking() {
+ mPendingFirstDownKeyEvent = null;
+ mIsLongPressing = false;
+ mLongPressTimeoutRunnable = null;
+ }
+
+ private KeyEvent createCanceledKeyEvent(KeyEvent keyEvent) {
+ KeyEvent upEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP);
+ return KeyEvent.changeTimeRepeat(upEvent, System.currentTimeMillis(), 0,
+ KeyEvent.FLAG_CANCELED);
+ }
+
+ private boolean isFirstLongPressKeyEvent(KeyEvent keyEvent) {
+ return ((keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0)
+ && keyEvent.getRepeatCount() == 1;
+ }
+
+ private boolean isFirstDownKeyEvent(KeyEvent keyEvent) {
+ return keyEvent.getAction() == KeyEvent.ACTION_DOWN && keyEvent.getRepeatCount() == 0;
+ }
+
+ private void dispatchDownAndUpKeyEventsLocked(String packageName, int pid, int uid,
+ boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
+ KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
+ dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
+ downEvent, needWakeLock);
+ dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
+ keyEvent, needWakeLock);
}
private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
@@ -2149,15 +2265,11 @@
MediaSessionRecord session = null;
// Retrieve custom session for key event if it exists.
- if (mCustomMediaKeyDispatcher != null && mGetSessionForKeyEventMethod != null) {
- try {
- Object tokenObject = mGetSessionForKeyEventMethod.invoke(
- mCustomMediaKeyDispatcher, keyEvent, uid, asSystemService);
- if (tokenObject != null) {
- session = getMediaSessionRecordLocked((MediaSession.Token) tokenObject);
- }
- } catch (InvocationTargetException | IllegalAccessException e) {
- Log.w(TAG, "Encountered problem while using reflection", e);
+ if (mCustomMediaKeyDispatcher != null) {
+ MediaSession.Token token = mCustomMediaKeyDispatcher.getSessionForKeyEvent(
+ keyEvent, uid, asSystemService);
+ if (token != null) {
+ session = getMediaSessionRecordLocked(token);
}
}
@@ -2312,12 +2424,11 @@
mHandled = true;
mHandler.removeCallbacks(this);
synchronized (mLock) {
- if (!isGlobalPriorityActiveLocked()
- && isVoiceKey(mKeyEvent.getKeyCode())) {
- handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
+ if (isGlobalPriorityActiveLocked()) {
+ dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
mKeyEvent, mNeedWakeLock);
} else {
- dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
+ handleKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
mKeyEvent, mNeedWakeLock);
}
}
diff --git a/services/core/java/com/android/server/media/SessionPolicyProvider.java b/services/core/java/com/android/server/media/SessionPolicyProvider.java
index 40a3d2d..5f02a07 100644
--- a/services/core/java/com/android/server/media/SessionPolicyProvider.java
+++ b/services/core/java/com/android/server/media/SessionPolicyProvider.java
@@ -29,6 +29,7 @@
* Note: When instantiating this class, {@link MediaSessionService} will only use the constructor
* without any parameters.
*/
+// TODO: Move this class to apex/media/
public abstract class SessionPolicyProvider {
@IntDef(value = {
SESSION_POLICY_IGNORE_BUTTON_RECEIVER,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d2481b7..e4b4bfd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5941,8 +5941,25 @@
|| shouldFilterApplicationLocked(ps2, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- return compareSignatures(p1.getSigningDetails().signatures,
- p2.getSigningDetails().signatures);
+ SigningDetails p1SigningDetails = p1.getSigningDetails();
+ SigningDetails p2SigningDetails = p2.getSigningDetails();
+ int result = compareSignatures(p1SigningDetails.signatures,
+ p2SigningDetails.signatures);
+ // To support backwards compatibility with clients of this API expecting pre-key
+ // rotation results if either of the packages has a signing lineage the oldest signer
+ // in the lineage is used for signature verification.
+ if (result != PackageManager.SIGNATURE_MATCH && (
+ p1SigningDetails.hasPastSigningCertificates()
+ || p2SigningDetails.hasPastSigningCertificates())) {
+ Signature[] p1Signatures = p1SigningDetails.hasPastSigningCertificates()
+ ? new Signature[]{p1SigningDetails.pastSigningCertificates[0]}
+ : p1SigningDetails.signatures;
+ Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates()
+ ? new Signature[]{p2SigningDetails.pastSigningCertificates[0]}
+ : p2SigningDetails.signatures;
+ result = compareSignatures(p1Signatures, p2Signatures);
+ }
+ return result;
}
}
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index c2ecd41..735a9e4 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -161,6 +161,10 @@
private void configureSettings() {
ContentResolver cr = getContext().getContentResolver();
+ // Stop ADB before we enable it, otherwise on userdebug/eng builds, the keys won't have
+ // registered with adbd, and it will prompt the user to confirm the keys.
+ Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 0);
+
// Disable the TTL for ADB keys before enabling ADB
Settings.Global.putLong(cr, Settings.Global.ADB_ALLOWED_CONNECTION_TIME, 0);
Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1);
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index e100ff8..4cdc172 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -68,6 +68,11 @@
private Set<Integer> mUsingFrontendIds = new HashSet<>();
/**
+ * List of the Lnb ids that are used by the current client.
+ */
+ private Set<Integer> mUsingLnbIds = new HashSet<>();
+
+ /**
* Optional arbitrary priority value given by the client.
*
* <p>This value can override the default priorotiy calculated from
@@ -131,21 +136,49 @@
mUsingFrontendIds.add(frontendId);
}
- public Iterable<Integer> getInUseFrontendIds() {
+ public Set<Integer> getInUseFrontendIds() {
return mUsingFrontendIds;
}
/**
* Called when the client released a frontend.
*
- * <p>This could happen when client resource reclaimed.
- *
* @param frontendId being released.
*/
public void releaseFrontend(int frontendId) {
mUsingFrontendIds.remove(frontendId);
}
+ /**
+ * Set when the client starts to use an Lnb.
+ *
+ * @param lnbId being used.
+ */
+ public void useLnb(int lnbId) {
+ mUsingLnbIds.add(lnbId);
+ }
+
+ public Set<Integer> getInUseLnbIds() {
+ return mUsingLnbIds;
+ }
+
+ /**
+ * Called when the client released an lnb.
+ *
+ * @param lnbId being released.
+ */
+ public void releaseLnb(int lnbId) {
+ mUsingLnbIds.remove(lnbId);
+ }
+
+ /**
+ * Called to reclaim all the resources being used by the current client.
+ */
+ public void reclaimAllResources() {
+ mUsingFrontendIds.clear();
+ mUsingLnbIds.clear();
+ }
+
@Override
public String toString() {
return "ClientProfile[id=" + this.mId + ", tvInputSessionId=" + this.mTvInputSessionId
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
index 56f6159..7ea62b2 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
@@ -27,14 +27,7 @@
*
* @hide
*/
-public final class FrontendResource {
- public static final int INVALID_OWNER_ID = -1;
-
- /**
- * Id of the current frontend. Should not be changed and should be aligned with the driver level
- * implementation.
- */
- private final int mId;
+public final class FrontendResource extends TunerResourceBasic {
/**
* see {@link android.media.tv.tuner.frontend.FrontendSettings.Type}
@@ -51,28 +44,12 @@
*/
private Set<Integer> mExclusiveGroupMemberFeIds = new HashSet<>();
- /**
- * If the current resource is in use. Once resources under the same exclusive group id is in use
- * all other resources in the same group would be considered in use.
- */
- private boolean mIsInUse;
-
- /**
- * The owner client's id if this resource is occupied. Owner of the resource under the same
- * exclusive group id would be considered as the whole group's owner.
- */
- private int mOwnerClientId = INVALID_OWNER_ID;
-
private FrontendResource(Builder builder) {
- this.mId = builder.mId;
+ super(builder);
this.mType = builder.mType;
this.mExclusiveGroupId = builder.mExclusiveGroupId;
}
- public int getId() {
- return mId;
- }
-
public int getType() {
return mType;
}
@@ -112,32 +89,6 @@
mExclusiveGroupMemberFeIds.remove(id);
}
- public boolean isInUse() {
- return mIsInUse;
- }
-
- public int getOwnerClientId() {
- return mOwnerClientId;
- }
-
- /**
- * Set an owner client on the resource.
- *
- * @param ownerClientId the id of the owner client.
- */
- public void setOwner(int ownerClientId) {
- mIsInUse = true;
- mOwnerClientId = ownerClientId;
- }
-
- /**
- * Remove an owner client from the resource.
- */
- public void removeOwner() {
- mIsInUse = false;
- mOwnerClientId = INVALID_OWNER_ID;
- }
-
@Override
public String toString() {
return "FrontendResource[id=" + this.mId + ", type=" + this.mType
@@ -149,13 +100,12 @@
/**
* Builder class for {@link FrontendResource}.
*/
- public static class Builder {
- private final int mId;
+ public static class Builder extends TunerResourceBasic.Builder {
@Type private int mType;
private int mExclusiveGroupId;
Builder(int id) {
- this.mId = id;
+ super(id);
}
/**
@@ -183,6 +133,7 @@
*
* @return {@link FrontendResource}.
*/
+ @Override
public FrontendResource build() {
FrontendResource frontendResource = new FrontendResource(this);
return frontendResource;
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
new file mode 100644
index 0000000..345b4b2
--- /dev/null
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.tv.tunerresourcemanager;
+
+/**
+ * An Lnb resource object used by the Tuner Resource Manager to record the tuner Lnb
+ * information.
+ *
+ * @hide
+ */
+public final class LnbResource extends TunerResourceBasic {
+
+ private LnbResource(Builder builder) {
+ super(builder);
+ }
+
+ @Override
+ public String toString() {
+ return "LnbResource[id=" + this.mId
+ + ", isInUse=" + this.mIsInUse + ", ownerClientId=" + this.mOwnerClientId + "]";
+ }
+
+ /**
+ * Builder class for {@link LnbResource}.
+ */
+ public static class Builder extends TunerResourceBasic.Builder {
+
+ Builder(int id) {
+ super(id);
+ }
+
+ /**
+ * Build a {@link LnbResource}.
+ *
+ * @return {@link LnbResource}.
+ */
+ @Override
+ public LnbResource build() {
+ LnbResource lnb = new LnbResource(this);
+ return lnb;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
new file mode 100644
index 0000000..7f133c3
--- /dev/null
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.tv.tunerresourcemanager;
+
+import static android.media.tv.tunerresourcemanager.TunerResourceManager.INVALID_OWNER_ID;
+
+/**
+ * A Tuner resource basic object used by the Tuner Resource Manager to record the resource
+ * information.
+ *
+ * @hide
+ */
+public class TunerResourceBasic {
+ /**
+ * Id of the current resource. Should not be changed and should be aligned with the driver level
+ * implementation.
+ */
+ final int mId;
+
+ /**
+ * If the current resource is in use.
+ */
+ boolean mIsInUse;
+
+ /**
+ * The owner client's id if this resource is occupied.
+ */
+ int mOwnerClientId = INVALID_OWNER_ID;
+
+ TunerResourceBasic(Builder builder) {
+ this.mId = builder.mId;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public boolean isInUse() {
+ return mIsInUse;
+ }
+
+ public int getOwnerClientId() {
+ return mOwnerClientId;
+ }
+
+ /**
+ * Set an owner client on the resource.
+ *
+ * @param ownerClientId the id of the owner client.
+ */
+ public void setOwner(int ownerClientId) {
+ mIsInUse = true;
+ mOwnerClientId = ownerClientId;
+ }
+
+ /**
+ * Remove an owner client from the resource.
+ */
+ public void removeOwner() {
+ mIsInUse = false;
+ mOwnerClientId = INVALID_OWNER_ID;
+ }
+
+ /**
+ * Builder class for {@link TunerResourceBasic}.
+ */
+ public static class Builder {
+ private final int mId;
+
+ Builder(int id) {
+ this.mId = id;
+ }
+
+ /**
+ * Build a {@link TunerResourceBasic}.
+ *
+ * @return {@link TunerResourceBasic}.
+ */
+ public TunerResourceBasic build() {
+ TunerResourceBasic resource = new TunerResourceBasic(this);
+ return resource;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 6dded00..7231813 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -63,6 +63,8 @@
// Map of the current available frontend resources
private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>();
+ // Map of the current available lnb resources
+ private Map<Integer, LnbResource> mLnbResources = new HashMap<>();
@GuardedBy("mLock")
private Map<Integer, ResourcesReclaimListenerRecord> mListeners = new HashMap<>();
@@ -164,12 +166,13 @@
}
@Override
- public void setLnbInfoList(int[] lnbIds) {
+ public void setLnbInfoList(int[] lnbIds) throws RemoteException {
enforceTrmAccessPermission("setLnbInfoList");
- if (DEBUG) {
- for (int i = 0; i < lnbIds.length; i++) {
- Slog.d(TAG, "updateLnbInfo(lnbId=" + lnbIds[i] + ")");
- }
+ if (lnbIds == null) {
+ throw new RemoteException("Lnb id list can't be null");
+ }
+ synchronized (mLock) {
+ setLnbInfoListInternal(lnbIds);
}
}
@@ -237,26 +240,45 @@
}
@Override
- public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) {
+ public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle)
+ throws RemoteException {
enforceTunerAccessPermission("requestLnb");
enforceTrmAccessPermission("requestLnb");
- if (DEBUG) {
- Slog.d(TAG, "requestLnb(request=" + request + ")");
+ if (lnbHandle == null) {
+ throw new RemoteException("lnbHandle can't be null");
}
- return true;
+ synchronized (mLock) {
+ try {
+ return requestLnbInternal(request, lnbHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
@Override
- public void releaseFrontend(int frontendId) {
+ public void releaseFrontend(int frontendHandle, int clientId) throws RemoteException {
enforceTunerAccessPermission("releaseFrontend");
enforceTrmAccessPermission("releaseFrontend");
- if (DEBUG) {
- Slog.d(TAG, "releaseFrontend(id=" + frontendId + ")");
+ if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
+ frontendHandle)) {
+ throw new RemoteException("frontendHandle can't be invalid");
+ }
+ int frontendId = getResourceIdFromHandle(frontendHandle);
+ FrontendResource fe = getFrontendResource(frontendId);
+ if (fe == null) {
+ throw new RemoteException("Releasing frontend does not exist.");
+ }
+ if (fe.getOwnerClientId() != clientId) {
+ throw new RemoteException("Client is not the current owner of the releasing fe.");
+ }
+ synchronized (mLock) {
+ releaseFrontendInternal(fe);
}
}
@Override
- public void releaseDemux(int demuxHandle) {
+ public void releaseDemux(int demuxHandle, int clientId) {
enforceTunerAccessPermission("releaseDemux");
enforceTrmAccessPermission("releaseDemux");
if (DEBUG) {
@@ -265,7 +287,7 @@
}
@Override
- public void releaseDescrambler(int descramblerHandle) {
+ public void releaseDescrambler(int descramblerHandle, int clientId) {
enforceTunerAccessPermission("releaseDescrambler");
enforceTrmAccessPermission("releaseDescrambler");
if (DEBUG) {
@@ -274,7 +296,7 @@
}
@Override
- public void releaseCasSession(int sessionResourceId) {
+ public void releaseCasSession(int sessionResourceId, int clientId) {
enforceTrmAccessPermission("releaseCasSession");
if (DEBUG) {
Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")");
@@ -282,11 +304,22 @@
}
@Override
- public void releaseLnb(int lnbId) {
+ public void releaseLnb(int lnbHandle, int clientId) throws RemoteException {
enforceTunerAccessPermission("releaseLnb");
enforceTrmAccessPermission("releaseLnb");
- if (DEBUG) {
- Slog.d(TAG, "releaseLnb(lnbId=" + lnbId + ")");
+ if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) {
+ throw new RemoteException("lnbHandle can't be invalid");
+ }
+ int lnbId = getResourceIdFromHandle(lnbHandle);
+ LnbResource lnb = getLnbResource(lnbId);
+ if (lnb == null) {
+ throw new RemoteException("Releasing lnb does not exist.");
+ }
+ if (lnb.getOwnerClientId() != clientId) {
+ throw new RemoteException("Client is not the current owner of the releasing lnb.");
+ }
+ synchronized (mLock) {
+ releaseLnbInternal(lnb);
}
}
@@ -393,7 +426,6 @@
}
}
- // TODO check if the removing resource is in use or not. Handle the conflict.
for (int removingId : updatingFrontendIds) {
// update the exclusive group id member list
removeFrontendResource(removingId);
@@ -401,6 +433,38 @@
}
@VisibleForTesting
+ protected void setLnbInfoListInternal(int[] lnbIds) {
+ if (DEBUG) {
+ for (int i = 0; i < lnbIds.length; i++) {
+ Slog.d(TAG, "updateLnbInfo(lnbId=" + lnbIds[i] + ")");
+ }
+ }
+
+ // A set to record the Lnbs pending on updating. Ids will be removed
+ // from this set once its updating finished. Any lnb left in this set when all
+ // the updates are done will be removed from mLnbResources.
+ Set<Integer> updatingLnbIds = new HashSet<>(getLnbResources().keySet());
+
+ // Update lnbResources map and other mappings accordingly
+ for (int i = 0; i < lnbIds.length; i++) {
+ if (getLnbResource(lnbIds[i]) != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Lnb id=" + lnbIds[i] + "exists.");
+ }
+ updatingLnbIds.remove(lnbIds[i]);
+ } else {
+ // Add a new lnb resource
+ LnbResource newLnb = new LnbResource.Builder(lnbIds[i]).build();
+ addLnbResource(newLnb);
+ }
+ }
+
+ for (int removingId : updatingLnbIds) {
+ removeLnbResource(removingId);
+ }
+ }
+
+ @VisibleForTesting
protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle)
throws RemoteException {
if (DEBUG) {
@@ -452,10 +516,12 @@
// When all the resources are occupied, grant the lowest priority resource if the
// request client has higher priority.
if (inUseLowestPriorityFrId > -1 && (requestClient.getPriority() > currentLowestPriority)) {
+ if (!reclaimResource(getFrontendResource(inUseLowestPriorityFrId).getOwnerClientId(),
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
+ return false;
+ }
frontendHandle[0] = generateResourceHandle(
TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, inUseLowestPriorityFrId);
- reclaimFrontendResource(getFrontendResource(
- inUseLowestPriorityFrId).getOwnerClientId());
updateFrontendClientMappingOnNewGrant(inUseLowestPriorityFrId, request.getClientId());
return true;
}
@@ -464,10 +530,85 @@
}
@VisibleForTesting
+ protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle)
+ throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "requestLnb(request=" + request + ")");
+ }
+
+ lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ if (!checkClientExists(request.getClientId())) {
+ Slog.e(TAG, "Request lnb from unregistered client:" + request.getClientId());
+ return false;
+ }
+ ClientProfile requestClient = getClientProfile(request.getClientId());
+ int grantingLnbId = -1;
+ int inUseLowestPriorityLnbId = -1;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ for (LnbResource lnb : getLnbResources().values()) {
+ if (!lnb.isInUse()) {
+ // Grant the unused lnb with lower id first
+ grantingLnbId = lnb.getId();
+ break;
+ } else {
+ // Record the lnb id with the lowest client priority among all the
+ // in use lnb when no available lnb has been found.
+ int priority = getOwnerClientPriority(lnb);
+ if (currentLowestPriority > priority) {
+ inUseLowestPriorityLnbId = lnb.getId();
+ currentLowestPriority = priority;
+ }
+ }
+ }
+
+ // Grant Lnb when there is unused resource.
+ if (grantingLnbId > -1) {
+ lnbHandle[0] = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, grantingLnbId);
+ updateLnbClientMappingOnNewGrant(grantingLnbId, request.getClientId());
+ return true;
+ }
+
+ // When all the resources are occupied, grant the lowest priority resource if the
+ // request client has higher priority.
+ if (inUseLowestPriorityLnbId > -1
+ && (requestClient.getPriority() > currentLowestPriority)) {
+ if (!reclaimResource(getLnbResource(inUseLowestPriorityLnbId).getOwnerClientId(),
+ TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) {
+ return false;
+ }
+ lnbHandle[0] = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, inUseLowestPriorityLnbId);
+ updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbId, request.getClientId());
+ return true;
+ }
+
+ return false;
+ }
+
+ @VisibleForTesting
+ void releaseFrontendInternal(FrontendResource fe) {
+ if (DEBUG) {
+ Slog.d(TAG, "releaseFrontend(id=" + fe.getId() + ")");
+ }
+ updateFrontendClientMappingOnRelease(fe);
+ }
+
+ @VisibleForTesting
+ void releaseLnbInternal(LnbResource lnb) {
+ if (DEBUG) {
+ Slog.d(TAG, "releaseLnb(lnbId=" + lnb.getId() + ")");
+ }
+ updateLnbClientMappingOnRelease(lnb);
+ }
+
+ @VisibleForTesting
boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
if (DEBUG) {
Slog.d(TAG, "requestDemux(request=" + request + ")");
}
+ // There are enough Demux resources, so we don't manage Demux in R.
demuxHandle[0] = generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
return true;
}
@@ -477,6 +618,7 @@
if (DEBUG) {
Slog.d(TAG, "requestDescrambler(request=" + request + ")");
}
+ // There are enough Descrambler resources, so we don't manage Descrambler in R.
descramblerHandle[0] =
generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DESCRAMBLER, 0);
return true;
@@ -530,12 +672,21 @@
}
@VisibleForTesting
- protected void reclaimFrontendResource(int reclaimingId) {
- try {
- mListeners.get(reclaimingId).getListener().onReclaimResources();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingId, e);
+ protected boolean reclaimResource(int reclaimingClientId,
+ @TunerResourceManager.TunerResourceType int resourceType) {
+ if (DEBUG) {
+ Slog.d(TAG, "Reclaiming resources because higher priority client request resource type "
+ + resourceType);
}
+ try {
+ mListeners.get(reclaimingClientId).getListener().onReclaimResources();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e);
+ return false;
+ }
+ ClientProfile profile = getClientProfile(reclaimingClientId);
+ reclaimingResourcesFromClient(profile);
+ return true;
}
@VisibleForTesting
@@ -568,14 +719,37 @@
}
}
+ private void updateFrontendClientMappingOnRelease(@NonNull FrontendResource releasingFrontend) {
+ ClientProfile ownerProfile = getClientProfile(releasingFrontend.getOwnerClientId());
+ releasingFrontend.removeOwner();
+ ownerProfile.releaseFrontend(releasingFrontend.getId());
+ for (int exclusiveGroupMember : releasingFrontend.getExclusiveGroupMemberFeIds()) {
+ getFrontendResource(exclusiveGroupMember).removeOwner();
+ ownerProfile.releaseFrontend(exclusiveGroupMember);
+ }
+ }
+
+ private void updateLnbClientMappingOnNewGrant(int grantingId, int ownerClientId) {
+ LnbResource grantingLnb = getLnbResource(grantingId);
+ ClientProfile ownerProfile = getClientProfile(ownerClientId);
+ grantingLnb.setOwner(ownerClientId);
+ ownerProfile.useLnb(grantingId);
+ }
+
+ private void updateLnbClientMappingOnRelease(@NonNull LnbResource releasingLnb) {
+ ClientProfile ownerProfile = getClientProfile(releasingLnb.getOwnerClientId());
+ releasingLnb.removeOwner();
+ ownerProfile.releaseLnb(releasingLnb.getId());
+ }
+
/**
- * Get the owner client's priority from the frontend id.
+ * Get the owner client's priority from the resource id.
*
- * @param frontend an in use frontend.
- * @return the priority of the owner client of the frontend.
+ * @param resource a in use tuner resource.
+ * @return the priority of the owner client of the resource.
*/
- private int getOwnerClientPriority(FrontendResource frontend) {
- return getClientProfile(frontend.getOwnerClientId()).getPriority();
+ private int getOwnerClientPriority(TunerResourceBasic resource) {
+ return getClientProfile(resource.getOwnerClientId()).getPriority();
}
@VisibleForTesting
@@ -609,6 +783,9 @@
private void removeFrontendResource(int removingId) {
FrontendResource fe = getFrontendResource(removingId);
+ if (fe.isInUse()) {
+ releaseFrontendInternal(fe);
+ }
for (int excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
getFrontendResource(excGroupmemberFeId)
.removeExclusiveGroupMemberFeId(fe.getId());
@@ -618,6 +795,30 @@
@VisibleForTesting
@Nullable
+ protected LnbResource getLnbResource(int lnbId) {
+ return mLnbResources.get(lnbId);
+ }
+
+ @VisibleForTesting
+ protected Map<Integer, LnbResource> getLnbResources() {
+ return mLnbResources;
+ }
+
+ private void addLnbResource(LnbResource newLnb) {
+ // Update resource list and available id list
+ mLnbResources.put(newLnb.getId(), newLnb);
+ }
+
+ private void removeLnbResource(int removingId) {
+ LnbResource lnb = getLnbResource(removingId);
+ if (lnb.isInUse()) {
+ releaseLnbInternal(lnb);
+ }
+ mLnbResources.remove(removingId);
+ }
+
+ @VisibleForTesting
+ @Nullable
protected ClientProfile getClientProfile(int clientId) {
return mClientProfiles.get(clientId);
}
@@ -639,6 +840,16 @@
mListeners.remove(clientId);
}
+ private void reclaimingResourcesFromClient(ClientProfile profile) {
+ for (Integer feId : profile.getInUseFrontendIds()) {
+ getFrontendResource(feId).removeOwner();
+ }
+ for (Integer lnbId : profile.getInUseLnbIds()) {
+ getLnbResource(lnbId).removeOwner();
+ }
+ profile.reclaimAllResources();
+ }
+
@VisibleForTesting
protected boolean checkClientExists(int clientId) {
return mClientProfiles.keySet().contains(clientId);
@@ -651,6 +862,22 @@
| (mResourceRequestCount++ & 0xffff);
}
+ @VisibleForTesting
+ protected int getResourceIdFromHandle(int resourceHandle) {
+ if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return resourceHandle;
+ }
+ return (resourceHandle & 0x00ff0000) >> 16;
+ }
+
+ private boolean validateResourceHandle(int resourceType, int resourceHandle) {
+ if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE
+ || ((resourceHandle & 0xff000000) >> 24) != resourceType) {
+ return false;
+ }
+ return true;
+ }
+
private void enforceTrmAccessPermission(String apiName) {
getContext().enforceCallingPermission("android.permission.TUNER_RESOURCE_ACCESS",
TAG + ": " + apiName);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 4fe5843..3f9f95c 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -726,8 +726,7 @@
} else {
final Region dirtyRegion = mTempRegion3;
dirtyRegion.set(mMagnificationRegion);
- dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION);
- dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
+ dirtyRegion.op(mOldMagnificationRegion, Region.Op.XOR);
dirtyRegion.getBounds(dirtyRect);
mWindow.invalidate(dirtyRect);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 521ffa5..7a5ff41 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -292,6 +292,7 @@
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
+import android.window.WindowContainerToken;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -432,6 +433,8 @@
final boolean stateNotNeeded; // As per ActivityInfo.flags
@VisibleForTesting
int mHandoverLaunchDisplayId = INVALID_DISPLAY; // Handover launch display id to next activity.
+ @VisibleForTesting
+ TaskDisplayArea mHandoverTaskDisplayArea; // Handover launch task display area.
private final boolean componentSpecified; // did caller specify an explicit component?
final boolean rootVoiceInteraction; // was this the root activity of a voice interaction?
@@ -1657,7 +1660,11 @@
if (usageReport != null) {
appTimeTracker = new AppTimeTracker(usageReport);
}
- // Gets launch display id from options. It returns INVALID_DISPLAY if not set.
+ // Gets launch task display area and display id from options. Returns
+ // null/INVALID_DISPLAY if not set.
+ final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
+ mHandoverTaskDisplayArea = daToken != null
+ ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
mHandoverLaunchDisplayId = options.getLaunchDisplayId();
}
}
@@ -3762,7 +3769,8 @@
pendingOptions.getPackageName(),
pendingOptions.getCustomEnterResId(),
pendingOptions.getCustomExitResId(),
- pendingOptions.getOnAnimationStartListener());
+ pendingOptions.getAnimationStartedListener(),
+ pendingOptions.getAnimationFinishedListener());
break;
case ANIM_CLIP_REVEAL:
displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
@@ -3792,7 +3800,7 @@
final GraphicBuffer buffer = pendingOptions.getThumbnail();
displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getOnAnimationStartListener(),
+ pendingOptions.getAnimationStartedListener(),
scaleUp);
if (intent.getSourceBounds() == null && buffer != null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
@@ -3808,19 +3816,19 @@
pendingOptions.getSpecsFuture();
if (specsFuture != null) {
displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
- specsFuture, pendingOptions.getOnAnimationStartListener(),
+ specsFuture, pendingOptions.getAnimationStartedListener(),
animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
} else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
&& specs != null) {
displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
- specs, pendingOptions.getOnAnimationStartListener(),
+ specs, pendingOptions.getAnimationStartedListener(),
pendingOptions.getAnimationFinishedListener(), false);
} else {
displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
pendingOptions.getThumbnail(),
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight(),
- pendingOptions.getOnAnimationStartListener(),
+ pendingOptions.getAnimationStartedListener(),
(animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 3bccced..fe9e5f3c 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1053,6 +1053,13 @@
return true;
}
+ /** Check if caller is allowed to launch activities on specified task display area. */
+ boolean isCallerAllowedToLaunchOnTaskDisplayArea(int callingPid, int callingUid,
+ TaskDisplayArea taskDisplayArea, ActivityInfo aInfo) {
+ return isCallerAllowedToLaunchOnDisplay(callingPid, callingUid,
+ taskDisplayArea != null ? taskDisplayArea.getDisplayId() : DEFAULT_DISPLAY, aInfo);
+ }
+
/** Check if caller is allowed to launch activities on specified display. */
boolean isCallerAllowedToLaunchOnDisplay(int callingPid, int callingUid, int launchDisplayId,
ActivityInfo aInfo) {
@@ -2133,18 +2140,6 @@
WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION);
}
- // TODO(b/152116619): Remove after complete switch to TaskDisplayArea
- void handleNonResizableTaskIfNeeded(Task task, int preferredWindowingMode,
- int preferredDisplayId, ActivityStack actualStack) {
- final DisplayContent preferredDisplayContent = mRootWindowContainer
- .getDisplayContent(preferredDisplayId);
- final TaskDisplayArea preferredDisplayArea = preferredDisplayContent != null
- ? preferredDisplayContent.getDefaultTaskDisplayArea()
- : null;
- handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayArea,
- actualStack);
- }
-
void handleNonResizableTaskIfNeeded(Task task, int preferredWindowingMode,
TaskDisplayArea preferredTaskDisplayArea, ActivityStack actualStack) {
handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredTaskDisplayArea,
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index ad54356..7fad395 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -184,8 +184,8 @@
}
final int displayId = taskDisplayArea.getDisplayId();
options.setLaunchDisplayId(displayId);
- // TODO(b/152116619): Enable after complete switch to WindowContainerToken
- //options.setLaunchWindowContainerToken(taskDisplayArea.getWindowContainerToken());
+ options.setLaunchTaskDisplayArea(taskDisplayArea.mRemoteToken
+ .toWindowContainerToken());
// The home activity will be started later, defer resuming to avoid unneccerary operations
// (e.g. start home recursively) when creating home stack.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 0bd1aca..0754a34 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -55,7 +55,6 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.INVALID_UID;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
@@ -167,8 +166,8 @@
private int mStartFlags;
private ActivityRecord mSourceRecord;
- // The display to launch the activity onto, barring any strong reason to do otherwise.
- private int mPreferredDisplayId;
+ // The task display area to launch the activity onto, barring any strong reason to do otherwise.
+ private TaskDisplayArea mPreferredTaskDisplayArea;
// The windowing mode to apply to the root task, if possible
private int mPreferredWindowingMode;
@@ -538,7 +537,7 @@
mDoResume = starter.mDoResume;
mStartFlags = starter.mStartFlags;
mSourceRecord = starter.mSourceRecord;
- mPreferredDisplayId = starter.mPreferredDisplayId;
+ mPreferredTaskDisplayArea = starter.mPreferredTaskDisplayArea;
mPreferredWindowingMode = starter.mPreferredWindowingMode;
mInTask = starter.mInTask;
@@ -1631,7 +1630,7 @@
// Update the recent tasks list immediately when the activity starts
mSupervisor.mRecentTasks.add(mStartActivity.getTask());
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),
- mPreferredWindowingMode, mPreferredDisplayId, mTargetStack);
+ mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetStack);
return START_SUCCESS;
}
@@ -1684,9 +1683,9 @@
mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r,
sourceRecord, mOptions, PHASE_BOUNDS, mLaunchParams);
- mPreferredDisplayId = mLaunchParams.hasPreferredDisplay()
- ? mLaunchParams.mPreferredDisplayId
- : DEFAULT_DISPLAY;
+ mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea()
+ ? mLaunchParams.mPreferredTaskDisplayArea
+ : mRootWindowContainer.getDefaultTaskDisplayArea();
mPreferredWindowingMode = mLaunchParams.mWindowingMode;
}
@@ -1703,10 +1702,12 @@
// Do not start home activity if it cannot be launched on preferred display. We are not
// doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
// fallback to launch on other displays.
- if (r.isActivityTypeHome() && !mRootWindowContainer.canStartHomeOnDisplay(r.info,
- mPreferredDisplayId, true /* allowInstrumenting */)) {
- Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
- return START_CANCELED;
+ if (r.isActivityTypeHome()) {
+ if (!mRootWindowContainer.canStartHomeOnDisplayArea(r.info, mPreferredTaskDisplayArea,
+ true /* allowInstrumenting */)) {
+ Slog.w(TAG, "Cannot launch home on display area " + mPreferredTaskDisplayArea);
+ return START_CANCELED;
+ }
}
if (mRestrictedBgActivity && (newTask || !targetTask.isUidPresent(mCallingUid))
@@ -1841,10 +1842,10 @@
&& top.attachedToProcess()
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
- // This allows home activity to automatically launch on secondary display when
- // display added, if home was the top activity on default display, instead of
- // sending new intent to the home activity on default display.
- && (!top.isActivityTypeHome() || top.getDisplayId() == mPreferredDisplayId);
+ // This allows home activity to automatically launch on secondary task display area
+ // when it was added, if home was the top activity on default task display area,
+ // instead of sending new intent to the home activity on default display area.
+ && (!top.isActivityTypeHome() || top.getDisplayArea() == mPreferredTaskDisplayArea);
if (!dontStart) {
return START_SUCCESS;
}
@@ -1866,7 +1867,7 @@
// Don't use mStartActivity.task to show the toast. We're not starting a new activity but
// reusing 'top'. Fields in mStartActivity may not be fully initialized.
mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(),
- mLaunchParams.mWindowingMode, mPreferredDisplayId, topStack);
+ mLaunchParams.mWindowingMode, mPreferredTaskDisplayArea, topStack);
return START_DELIVERED_TO_TOP;
}
@@ -2010,7 +2011,7 @@
mDoResume = false;
mStartFlags = 0;
mSourceRecord = null;
- mPreferredDisplayId = INVALID_DISPLAY;
+ mPreferredTaskDisplayArea = null;
mPreferredWindowingMode = WINDOWING_MODE_UNDEFINED;
mInTask = null;
@@ -2060,9 +2061,9 @@
// after we located a reusable task (which might be resided in another display).
mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r,
sourceRecord, options, PHASE_DISPLAY, mLaunchParams);
- mPreferredDisplayId = mLaunchParams.hasPreferredDisplay()
- ? mLaunchParams.mPreferredDisplayId
- : DEFAULT_DISPLAY;
+ mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea()
+ ? mLaunchParams.mPreferredTaskDisplayArea
+ : mRootWindowContainer.getDefaultTaskDisplayArea();
mPreferredWindowingMode = mLaunchParams.mWindowingMode;
mLaunchMode = r.launchMode;
@@ -2334,14 +2335,14 @@
} else {
// Otherwise find the best task to put the activity in.
intentActivity =
- mRootWindowContainer.findTask(mStartActivity, mPreferredDisplayId);
+ mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);
}
}
if (intentActivity != null
&& (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome())
- && intentActivity.getDisplayId() != mPreferredDisplayId) {
- // Do not reuse home activity on other displays.
+ && intentActivity.getDisplayArea() != mPreferredTaskDisplayArea) {
+ // Do not reuse home activity on other display areas.
intentActivity = null;
}
@@ -2363,7 +2364,7 @@
// the same behavior as if a new instance was being started, which means not bringing it
// to the front if the caller is not itself in the front.
final boolean differentTopTask;
- if (mPreferredDisplayId == mTargetStack.getDisplayId()) {
+ if (mTargetStack.getDisplayArea() == mPreferredTaskDisplayArea) {
final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
final ActivityRecord curTop = (focusStack == null)
? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d92f43b..409e6ff 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2003,7 +2003,7 @@
if (self.isState(
ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) {
self.getDisplay().mDisplayContent.mAppTransition.overridePendingAppTransition(
- packageName, enterAnim, exitAnim, null);
+ packageName, enterAnim, exitAnim, null, null);
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 6f1ddcd..c31d55c 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1417,23 +1417,7 @@
: new TranslateAnimation(0, fromX, 0, fromY);
set.addAnimation(scale);
set.addAnimation(translation);
-
- final IRemoteCallback callback = mAnimationFinishedCallback;
- if (callback != null) {
- set.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) { }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppTransition::doAnimationCallback, callback));
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) { }
- });
- }
+ setAppTransitionFinishedCallbackIfNeeded(set);
return set;
}
@@ -1671,6 +1655,7 @@
"applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s "
+ "isEntrance=%b Callers=%s",
a, appTransitionToString(transit), enter, Debug.getCallers(3));
+ setAppTransitionFinishedCallbackIfNeeded(a);
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1835,7 +1820,7 @@
}
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
- IRemoteCallback startedCallback) {
+ IRemoteCallback startedCallback, IRemoteCallback endedCallback) {
if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
@@ -1844,6 +1829,7 @@
mNextAppTransitionExit = exitAnim;
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
+ mAnimationFinishedCallback = endedCallback;
}
}
@@ -2331,6 +2317,25 @@
}
}
+ private void setAppTransitionFinishedCallbackIfNeeded(Animation anim) {
+ final IRemoteCallback callback = mAnimationFinishedCallback;
+ if (callback != null && anim != null) {
+ anim.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) { }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppTransition::doAnimationCallback, callback));
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) { }
+ });
+ }
+ }
+
void removeAppTransitionTimeoutCallbacks() {
mHandler.removeCallbacks(mHandleAppTransitionTimeoutRunnable);
}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index a9820ef..4cd3180 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
@@ -26,6 +25,7 @@
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.content.pm.ActivityInfo.WindowLayout;
import android.graphics.Rect;
@@ -108,11 +108,13 @@
if (activity != null && activity.requestedVrComponent != null) {
// Check if the Activity is a VR activity. If so, it should be launched in main display.
- result.mPreferredDisplayId = DEFAULT_DISPLAY;
+ result.mPreferredTaskDisplayArea = mService.mRootWindowContainer
+ .getDefaultTaskDisplayArea();
} else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
// Get the virtual display ID from ActivityTaskManagerService. If that's set we
// should always use that.
- result.mPreferredDisplayId = mService.mVr2dDisplayId;
+ result.mPreferredTaskDisplayArea = mService.mRootWindowContainer
+ .getDisplayContent(mService.mVr2dDisplayId).getDefaultTaskDisplayArea();
}
}
@@ -136,9 +138,10 @@
mService.deferWindowLayout();
try {
- if (mTmpParams.hasPreferredDisplay()
- && mTmpParams.mPreferredDisplayId != task.getStack().getDisplay().mDisplayId) {
- mService.moveStackToDisplay(task.getRootTaskId(), mTmpParams.mPreferredDisplayId);
+ if (mTmpParams.mPreferredTaskDisplayArea != null
+ && task.getDisplayArea() != mTmpParams.mPreferredTaskDisplayArea) {
+ mService.mRootWindowContainer.moveStackToTaskDisplayArea(task.getRootTaskId(),
+ mTmpParams.mPreferredTaskDisplayArea, true /* onTop */);
}
if (mTmpParams.hasWindowingMode()
@@ -184,8 +187,9 @@
/** The bounds within the parent container. */
final Rect mBounds = new Rect();
- /** The id of the display the {@link Task} would prefer to be on. */
- int mPreferredDisplayId;
+ /** The display area the {@link Task} would prefer to be on. */
+ @Nullable
+ TaskDisplayArea mPreferredTaskDisplayArea;
/** The windowing mode to be in. */
int mWindowingMode;
@@ -193,20 +197,20 @@
/** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
void reset() {
mBounds.setEmpty();
- mPreferredDisplayId = INVALID_DISPLAY;
+ mPreferredTaskDisplayArea = null;
mWindowingMode = WINDOWING_MODE_UNDEFINED;
}
/** Copies the values set on the passed in {@link LaunchParams}. */
void set(LaunchParams params) {
mBounds.set(params.mBounds);
- mPreferredDisplayId = params.mPreferredDisplayId;
+ mPreferredTaskDisplayArea = params.mPreferredTaskDisplayArea;
mWindowingMode = params.mWindowingMode;
}
/** Returns {@code true} if no values have been explicitly set. */
boolean isEmpty() {
- return mBounds.isEmpty() && mPreferredDisplayId == INVALID_DISPLAY
+ return mBounds.isEmpty() && mPreferredTaskDisplayArea == null
&& mWindowingMode == WINDOWING_MODE_UNDEFINED;
}
@@ -214,8 +218,8 @@
return mWindowingMode != WINDOWING_MODE_UNDEFINED;
}
- boolean hasPreferredDisplay() {
- return mPreferredDisplayId != INVALID_DISPLAY;
+ boolean hasPreferredTaskDisplayArea() {
+ return mPreferredTaskDisplayArea != null;
}
@Override
@@ -225,7 +229,7 @@
LaunchParams that = (LaunchParams) o;
- if (mPreferredDisplayId != that.mPreferredDisplayId) return false;
+ if (mPreferredTaskDisplayArea != that.mPreferredTaskDisplayArea) return false;
if (mWindowingMode != that.mWindowingMode) return false;
return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null;
}
@@ -233,7 +237,8 @@
@Override
public int hashCode() {
int result = mBounds != null ? mBounds.hashCode() : 0;
- result = 31 * result + mPreferredDisplayId;
+ result = 31 * result + (mPreferredTaskDisplayArea != null
+ ? mPreferredTaskDisplayArea.hashCode() : 0);
result = 31 * result + mWindowingMode;
return result;
}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 9371c0e..a974332 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -318,7 +318,9 @@
final DisplayContent display = mSupervisor.mRootWindowContainer.getDisplayContent(
persistableParams.mDisplayUniqueId);
if (display != null) {
- outParams.mPreferredDisplayId = display.mDisplayId;
+ // TODO(b/153764726): Investigate if task display area needs to be persisted vs
+ // always choosing the default one.
+ outParams.mPreferredTaskDisplayArea = display.getDefaultTaskDisplayArea();
}
outParams.mWindowingMode = persistableParams.mWindowingMode;
outParams.mBounds.set(persistableParams.mBounds);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e8f7ba5..6b9054b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -139,6 +139,7 @@
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
@@ -1519,8 +1520,7 @@
if (taskDisplayArea == getDefaultTaskDisplayArea()) {
homeIntent = mService.getHomeIntent();
aInfo = resolveHomeActivity(userId, homeIntent);
- } else if (taskDisplayArea.getDisplayId() == DEFAULT_DISPLAY
- || shouldPlaceSecondaryHomeOnDisplay(taskDisplayArea.getDisplayId())) {
+ } else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {
Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, taskDisplayArea);
aInfo = info.first;
homeIntent = info.second;
@@ -1529,7 +1529,7 @@
return false;
}
- if (!canStartHomeOnDisplay(aInfo, taskDisplayArea.getDisplayId(), allowInstrumenting)) {
+ if (!canStartHomeOnDisplayArea(aInfo, taskDisplayArea, allowInstrumenting)) {
return false;
}
@@ -1625,8 +1625,7 @@
}
if (aInfo != null) {
- if (!canStartHomeOnDisplay(aInfo, taskDisplayArea.getDisplayId(),
- false /* allowInstrumenting */)) {
+ if (!canStartHomeOnDisplayArea(aInfo, taskDisplayArea, false /* allowInstrumenting */)) {
aInfo = null;
}
}
@@ -1683,19 +1682,19 @@
}
/**
- * Check if the display is valid for secondary home activity.
- * @param displayId The id of the target display.
+ * Check if the display area is valid for secondary home activity.
+ * @param taskDisplayArea The target display area.
* @return {@code true} if allow to launch, {@code false} otherwise.
*/
- boolean shouldPlaceSecondaryHomeOnDisplay(int displayId) {
- if (displayId == DEFAULT_DISPLAY) {
+ boolean shouldPlaceSecondaryHomeOnDisplayArea(TaskDisplayArea taskDisplayArea) {
+ if (getDefaultTaskDisplayArea() == taskDisplayArea) {
throw new IllegalArgumentException(
- "shouldPlaceSecondaryHomeOnDisplay: Should not be DEFAULT_DISPLAY");
- } else if (displayId == INVALID_DISPLAY) {
+ "shouldPlaceSecondaryHomeOnDisplay: Should not be on default task container");
+ } else if (taskDisplayArea == null) {
return false;
}
- if (!mService.mSupportsMultiDisplay) {
+ if (taskDisplayArea.getDisplayId() != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
// Can't launch home on secondary display if device does not support multi-display.
return false;
}
@@ -1704,16 +1703,16 @@
mService.mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0) != 0;
if (!deviceProvisioned) {
- // Can't launch home on secondary display before device is provisioned.
+ // Can't launch home on secondary display areas before device is provisioned.
return false;
}
if (!StorageManager.isUserKeyUnlocked(mCurrentUser)) {
- // Can't launch home on secondary displays if device is still locked.
+ // Can't launch home on secondary display areas if device is still locked.
return false;
}
- final DisplayContent display = getDisplayContent(displayId);
+ final DisplayContent display = taskDisplayArea.getDisplayContent();
if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
// Can't launch home on display that doesn't support system decorations.
return false;
@@ -1725,11 +1724,11 @@
/**
* Check if home activity start should be allowed on a display.
* @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched.
- * @param displayId The id of the target display.
+ * @param taskDisplayArea The target display area.
* @param allowInstrumenting Whether launching home should be allowed if being instrumented.
* @return {@code true} if allow to launch, {@code false} otherwise.
*/
- boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId,
+ boolean canStartHomeOnDisplayArea(ActivityInfo homeInfo, TaskDisplayArea taskDisplayArea,
boolean allowInstrumenting) {
if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mService.mTopAction == null) {
@@ -1745,13 +1744,15 @@
return false;
}
+ final int displayId = taskDisplayArea != null ? taskDisplayArea.getDisplayId()
+ : INVALID_DISPLAY;
if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
&& displayId == mService.mVr2dDisplayId)) {
// No restrictions to default display or vr 2d display.
return true;
}
- if (!shouldPlaceSecondaryHomeOnDisplay(displayId)) {
+ if (!shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {
return false;
}
@@ -2208,15 +2209,14 @@
}
}
- ActivityRecord findTask(ActivityRecord r, int preferredDisplayId) {
+ ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
mTmpFindTaskResult.clear();
- // Looking up task on preferred display first
- final DisplayContent preferredDisplay = getDisplayContent(preferredDisplayId);
- if (preferredDisplay != null) {
- preferredDisplay.getDefaultTaskDisplayArea().findTaskLocked(r,
- true /* isPreferredDisplay */, mTmpFindTaskResult);
+ // Looking up task on preferred display area first
+ if (preferredTaskDisplayArea != null) {
+ preferredTaskDisplayArea.findTaskLocked(r, true /* isPreferredDisplay */,
+ mTmpFindTaskResult);
if (mTmpFindTaskResult.mIdealMatch) {
return mTmpFindTaskResult.mRecord;
}
@@ -2224,14 +2224,17 @@
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
- if (display.mDisplayId == preferredDisplayId) {
- continue;
- }
+ for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+ final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ if (taskDisplayArea == preferredTaskDisplayArea) {
+ continue;
+ }
- display.getDefaultTaskDisplayArea().findTaskLocked(r, false /* isPreferredDisplay */,
- mTmpFindTaskResult);
- if (mTmpFindTaskResult.mIdealMatch) {
- return mTmpFindTaskResult.mRecord;
+ taskDisplayArea.findTaskLocked(r, false /* isPreferredDisplay */,
+ mTmpFindTaskResult);
+ if (mTmpFindTaskResult.mIdealMatch) {
+ return mTmpFindTaskResult.mRecord;
+ }
}
}
@@ -2823,11 +2826,15 @@
int realCallingUid) {
int taskId = INVALID_TASK_ID;
int displayId = INVALID_DISPLAY;
+ TaskDisplayArea taskDisplayArea = null;
// We give preference to the launch preference in activity options.
if (options != null) {
taskId = options.getLaunchTaskId();
displayId = options.getLaunchDisplayId();
+ final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
+ taskDisplayArea = daToken != null
+ ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
}
// First preference for stack goes to the task Id set in the activity options. Use the stack
@@ -2846,30 +2853,34 @@
final int activityType = resolveActivityType(r, options, candidateTask);
ActivityStack stack = null;
- // Next preference for stack goes to the display Id set the candidate display.
- if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
- displayId = launchParams.mPreferredDisplayId;
+ // Next preference for stack goes to the taskDisplayArea candidate.
+ if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null) {
+ taskDisplayArea = launchParams.mPreferredTaskDisplayArea;
}
- final boolean canLaunchOnDisplayFromStartRequest =
- realCallingPid != 0 && realCallingUid > 0 && r != null
- && mStackSupervisor.canPlaceEntityOnDisplay(displayId, realCallingPid,
- realCallingUid, r.info);
- // Checking if the activity's launch caller, or the realCallerId of the activity from
- // start request (i.e. entity that invokes PendingIntent) is allowed to launch on the
- // display.
- if (displayId != INVALID_DISPLAY && (canLaunchOnDisplay(r, displayId)
- || canLaunchOnDisplayFromStartRequest)) {
- if (r != null) {
- stack = getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
- launchParams);
- if (stack != null) {
- return stack;
- }
+
+ if (taskDisplayArea == null && displayId != INVALID_DISPLAY) {
+ final DisplayContent displayContent = getDisplayContent(displayId);
+ if (displayContent != null) {
+ taskDisplayArea = displayContent.getDefaultTaskDisplayArea();
}
- final DisplayContent display = getDisplayContentOrCreate(displayId);
- if (display != null) {
+ }
+
+ if (taskDisplayArea != null) {
+ final int tdaDisplayId = taskDisplayArea.getDisplayId();
+ final boolean canLaunchOnDisplayFromStartRequest =
+ realCallingPid != 0 && realCallingUid > 0 && r != null
+ && mStackSupervisor.canPlaceEntityOnDisplay(tdaDisplayId,
+ realCallingPid, realCallingUid, r.info);
+ if (canLaunchOnDisplayFromStartRequest || canLaunchOnDisplay(r, tdaDisplayId)) {
+ if (r != null) {
+ final ActivityStack result = getValidLaunchStackInTaskDisplayArea(
+ taskDisplayArea, r, candidateTask, options, launchParams);
+ if (result != null) {
+ return result;
+ }
+ }
// Falling back to default task container
- final TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+ taskDisplayArea = taskDisplayArea.mDisplayContent.getDefaultTaskDisplayArea();
stack = taskDisplayArea.getOrCreateStack(r, options, candidateTask, activityType,
onTop);
if (stack != null) {
@@ -2936,40 +2947,37 @@
}
/**
- * Get a topmost stack on the display, that is a valid launch stack for specified activity.
+ * Get a topmost stack on the display area, that is a valid launch stack for specified activity.
* If there is no such stack, new dynamic stack can be created.
- * @param displayId Target display.
+ * @param taskDisplayArea Target display area.
* @param r Activity that should be launched there.
* @param candidateTask The possible task the activity might be put in.
* @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
*/
@VisibleForTesting
- ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
- @Nullable Task candidateTask, @Nullable ActivityOptions options,
+ ActivityStack getValidLaunchStackInTaskDisplayArea(@NonNull TaskDisplayArea taskDisplayArea,
+ @NonNull ActivityRecord r, @Nullable Task candidateTask,
+ @Nullable ActivityOptions options,
@Nullable LaunchParamsController.LaunchParams launchParams) {
- final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
- if (displayContent == null) {
- throw new IllegalArgumentException(
- "Display with displayId=" + displayId + " not found.");
- }
-
- if (!r.canBeLaunchedOnDisplay(displayId)) {
+ if (!r.canBeLaunchedOnDisplay(taskDisplayArea.getDisplayId())) {
return null;
}
- // If {@code r} is already in target display and its task is the same as the candidate task,
- // the intention should be getting a launch stack for the reusable activity, so we can use
- // the existing stack.
+ // If {@code r} is already in target display area and its task is the same as the candidate
+ // task, the intention should be getting a launch stack for the reusable activity, so we can
+ // use the existing stack.
if (candidateTask != null && (r.getTask() == null || r.getTask() == candidateTask)) {
- final int attachedDisplayId = r.getDisplayId();
- if (attachedDisplayId == INVALID_DISPLAY || attachedDisplayId == displayId) {
+ // TODO(b/153920825): Fix incorrect evaluation of attached state
+ final TaskDisplayArea attachedTaskDisplayArea = r.getTask() != null
+ ? r.getTask().getDisplayArea() : r.getDisplayArea();
+ if (attachedTaskDisplayArea == null || attachedTaskDisplayArea == taskDisplayArea) {
return candidateTask.getStack();
}
// Or the candidate task is already a root task that can be reused by reparenting
// it to the target display.
if (candidateTask.isRootTask()) {
final ActivityStack stack = candidateTask.getStack();
- stack.reparent(displayContent.getDefaultTaskDisplayArea(), true /* onTop */);
+ stack.reparent(taskDisplayArea, true /* onTop */);
return stack;
}
}
@@ -2984,39 +2992,30 @@
windowingMode = options != null ? options.getLaunchWindowingMode()
: r.getWindowingMode();
}
+ windowingMode = taskDisplayArea.validateWindowingMode(windowingMode, r, candidateTask,
+ r.getActivityType());
// Return the topmost valid stack on the display.
- for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx);
- final int validatedWindowingMode = taskDisplayArea
- .validateWindowingMode(windowingMode, r, candidateTask, r.getActivityType());
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- if (isValidLaunchStack(stack, r, validatedWindowingMode)) {
- return stack;
- }
+ for (int i = taskDisplayArea.getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(i);
+ if (isValidLaunchStack(stack, r, windowingMode)) {
+ return stack;
}
}
- // If there is no valid stack on the external display - check if new dynamic stack will do.
- if (displayId != DEFAULT_DISPLAY) {
+ // If there is no valid stack on the secondary display area - check if new dynamic stack
+ // will do.
+ if (taskDisplayArea != getDisplayContent(taskDisplayArea.getDisplayId())
+ .getDefaultTaskDisplayArea()) {
final int activityType =
options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
? options.getLaunchActivityType() : r.getActivityType();
- final TaskDisplayArea taskDisplayArea = displayContent.getDefaultTaskDisplayArea();
return taskDisplayArea.createStack(windowingMode, activityType, true /*onTop*/);
}
return null;
}
- ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
- @Nullable ActivityOptions options,
- @Nullable LaunchParamsController.LaunchParams launchParams) {
- return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options,
- launchParams);
- }
-
// TODO: Can probably be consolidated into getLaunchStack()...
private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r, int windowingMode) {
switch (stack.getActivityType()) {
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index a7b5368..b71ecbb 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -18,10 +18,11 @@
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.Display.INVALID_DISPLAY;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -36,6 +37,7 @@
import android.os.UserHandle;
import android.util.Slog;
import android.view.RemoteAnimationAdapter;
+import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
@@ -206,8 +208,20 @@
throw new SecurityException(msg);
}
}
- // Check if someone tries to launch an activity on a private display with a different
- // owner.
+ // Check if the caller is allowed to launch on the specified display area.
+ final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
+ final TaskDisplayArea taskDisplayArea = daToken != null
+ ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
+ if (aInfo != null && taskDisplayArea != null
+ && !supervisor.isCallerAllowedToLaunchOnTaskDisplayArea(callingPid, callingUid,
+ taskDisplayArea, aInfo)) {
+ final String msg = "Permission Denial: starting " + getIntentString(intent)
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with launchTaskDisplayArea=" + taskDisplayArea;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ // Check if the caller is allowed to launch on the specified display.
final int launchDisplayId = options.getLaunchDisplayId();
if (aInfo != null && launchDisplayId != INVALID_DISPLAY
&& !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ea5967a..299b3bf 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -433,7 +433,6 @@
static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
private int mForceHiddenFlags = 0;
-
SurfaceControl.Transaction mMainWindowSizeChangeTransaction;
private final FindRootHelper mFindRootHelper = new FindRootHelper();
@@ -3388,7 +3387,9 @@
final Intent baseIntent = getBaseIntent();
// Make a copy of base intent because this is like a snapshot info.
// Besides, {@link RecentTasks#getRecentTasksImpl} may modify it.
+ final int baseIntentFlags = baseIntent == null ? 0 : baseIntent.getFlags();
info.baseIntent = baseIntent == null ? new Intent() : baseIntent.cloneFilter();
+ info.baseIntent.setFlags(baseIntentFlags);
info.baseActivity = mReuseActivitiesReport.base != null
? mReuseActivitiesReport.base.intent.getComponent()
: null;
@@ -4261,7 +4262,7 @@
void setActivityWindowingMode(int windowingMode) {
PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setWindowingMode,
- PooledLambda.__(ActivityRecord.class), windowingMode);
+ PooledLambda.__(ActivityRecord.class), windowingMode);
forAllActivities(c);
c.recycle();
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index da4401a..11c20b6 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -48,6 +48,7 @@
import android.util.Slog;
import android.view.Gravity;
import android.view.View;
+import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.LaunchParamsController.LaunchParams;
@@ -134,13 +135,15 @@
return RESULT_SKIP;
}
- // STEP 1: Determine the display to launch the activity/task.
- final int displayId = getPreferredLaunchDisplay(task, options, source, currentParams);
- outParams.mPreferredDisplayId = displayId;
- DisplayContent display = mSupervisor.mRootWindowContainer.getDisplayContent(displayId);
+ // STEP 1: Determine the display area to launch the activity/task.
+ final TaskDisplayArea taskDisplayArea = getPreferredLaunchTaskDisplayArea(task,
+ options, source, currentParams);
+ outParams.mPreferredTaskDisplayArea = taskDisplayArea;
+ // TODO(b/152116619): Update the usages of display to use taskDisplayArea below.
+ final DisplayContent display = taskDisplayArea.mDisplayContent;
if (DEBUG) {
- appendLog("display-id=" + outParams.mPreferredDisplayId + " display-windowing-mode="
- + display.getWindowingMode());
+ appendLog("task-display-area=" + outParams.mPreferredTaskDisplayArea
+ + " display-area-windowing-mode=" + taskDisplayArea.getWindowingMode());
}
if (phase == PHASE_DISPLAY) {
@@ -210,8 +213,8 @@
// layout and display conditions are not contradictory to their suggestions. It's important
// to carry over their values because LaunchParamsController doesn't automatically do that.
if (!currentParams.isEmpty() && !hasInitialBounds
- && (!currentParams.hasPreferredDisplay()
- || displayId == currentParams.mPreferredDisplayId)) {
+ && (currentParams.mPreferredTaskDisplayArea == null
+ || currentParams.mPreferredTaskDisplayArea == taskDisplayArea)) {
// Only set windowing mode if display is in freeform. If the display is in fullscreen
// mode we should only launch a task in fullscreen mode.
if (currentParams.hasWindowingMode() && display.inFreeformWindowingMode()) {
@@ -270,19 +273,19 @@
: display.getWindowingMode();
if (fullyResolvedCurrentParam) {
if (resolvedMode == WINDOWING_MODE_FREEFORM) {
- // Make sure bounds are in the display if it's possibly in a different display.
- if (currentParams.mPreferredDisplayId != displayId) {
+ // Make sure bounds are in the display if it's possibly in a different display/area.
+ if (currentParams.mPreferredTaskDisplayArea != taskDisplayArea) {
adjustBoundsToFitInDisplay(display, outParams.mBounds);
}
// Even though we want to keep original bounds, we still don't want it to stomp on
// an existing task.
adjustBoundsToAvoidConflictInDisplay(display, outParams.mBounds);
}
- } else if (display.inFreeformWindowingMode()) {
+ } else if (taskDisplayArea.inFreeformWindowingMode()) {
if (source != null && source.inFreeformWindowingMode()
&& resolvedMode == WINDOWING_MODE_FREEFORM
&& outParams.mBounds.isEmpty()
- && source.getDisplayId() == display.getDisplayId()) {
+ && source.getDisplayArea() == taskDisplayArea) {
// Set bounds to be not very far from source activity.
cascadeBounds(source.getConfiguration().windowConfiguration.getBounds(),
display, outParams.mBounds);
@@ -293,54 +296,87 @@
return RESULT_CONTINUE;
}
- private int getPreferredLaunchDisplay(@Nullable Task task,
+ private TaskDisplayArea getPreferredLaunchTaskDisplayArea(@Nullable Task task,
@Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams) {
- if (!mSupervisor.mService.mSupportsMultiDisplay) {
- return DEFAULT_DISPLAY;
+ TaskDisplayArea taskDisplayArea = null;
+
+ final WindowContainerToken optionLaunchTaskDisplayAreaToken = options != null
+ ? options.getLaunchTaskDisplayArea() : null;
+ if (optionLaunchTaskDisplayAreaToken != null) {
+ taskDisplayArea = (TaskDisplayArea) WindowContainer.fromBinder(
+ optionLaunchTaskDisplayAreaToken.asBinder());
+ if (DEBUG) appendLog("display-area-from-option=" + taskDisplayArea);
}
- int displayId = INVALID_DISPLAY;
- final int optionLaunchId = options != null ? options.getLaunchDisplayId() : INVALID_DISPLAY;
- if (optionLaunchId != INVALID_DISPLAY) {
- if (DEBUG) appendLog("display-from-option=" + optionLaunchId);
- displayId = optionLaunchId;
+ // If task display area is not specified in options - try display id
+ if (taskDisplayArea == null) {
+ final int optionLaunchId =
+ options != null ? options.getLaunchDisplayId() : INVALID_DISPLAY;
+ if (optionLaunchId != INVALID_DISPLAY) {
+ final DisplayContent dc = mSupervisor.mRootWindowContainer
+ .getDisplayContent(optionLaunchId);
+ if (dc != null) {
+ taskDisplayArea = dc.getDefaultTaskDisplayArea();
+ if (DEBUG) appendLog("display-from-option=" + optionLaunchId);
+ }
+ }
}
- // If the source activity is a no-display activity, pass on the launch display id from
- // source activity as currently preferred.
- if (displayId == INVALID_DISPLAY && source != null && source.noDisplay) {
- displayId = source.mHandoverLaunchDisplayId;
- if (DEBUG) appendLog("display-from-no-display-source=" + displayId);
+ // If the source activity is a no-display activity, pass on the launch display area token
+ // from source activity as currently preferred.
+ if (taskDisplayArea == null && source != null
+ && source.noDisplay) {
+ taskDisplayArea = source.mHandoverTaskDisplayArea;
+ if (taskDisplayArea != null) {
+ if (DEBUG) appendLog("display-area-from-no-display-source=" + taskDisplayArea);
+ } else {
+ // Try handover display id
+ final int displayId = source.mHandoverLaunchDisplayId;
+ final DisplayContent dc =
+ mSupervisor.mRootWindowContainer.getDisplayContent(displayId);
+ if (dc != null) {
+ taskDisplayArea = dc.getDefaultTaskDisplayArea();
+ if (DEBUG) appendLog("display-from-no-display-source=" + displayId);
+ }
+ }
}
- ActivityStack stack =
- (displayId == INVALID_DISPLAY && task != null) ? task.getStack() : null;
+ ActivityStack stack = (taskDisplayArea == null && task != null)
+ ? task.getStack() : null;
if (stack != null) {
if (DEBUG) appendLog("display-from-task=" + stack.getDisplayId());
- displayId = stack.getDisplayId();
+ taskDisplayArea = stack.getDisplayArea();
}
- if (displayId == INVALID_DISPLAY && source != null) {
- final int sourceDisplayId = source.getDisplayId();
- if (DEBUG) appendLog("display-from-source=" + sourceDisplayId);
- displayId = sourceDisplayId;
+ if (taskDisplayArea == null && source != null) {
+ final TaskDisplayArea sourceDisplayArea = source.getDisplayArea();
+ if (DEBUG) appendLog("display-area-from-source=" + sourceDisplayArea);
+ taskDisplayArea = sourceDisplayArea;
}
- if (displayId == INVALID_DISPLAY && options != null) {
+ if (taskDisplayArea == null && options != null) {
final int callerDisplayId = options.getCallerDisplayId();
- if (DEBUG) appendLog("display-from-caller=" + callerDisplayId);
- displayId = callerDisplayId;
+ final DisplayContent dc =
+ mSupervisor.mRootWindowContainer.getDisplayContent(callerDisplayId);
+ if (dc != null) {
+ taskDisplayArea = dc.getDefaultTaskDisplayArea();
+ if (DEBUG) appendLog("display-from-caller=" + callerDisplayId);
+ }
}
- if (displayId != INVALID_DISPLAY
- && mSupervisor.mRootWindowContainer.getDisplayContent(displayId) == null) {
- displayId = currentParams.mPreferredDisplayId;
+ if (taskDisplayArea == null) {
+ taskDisplayArea = currentParams.mPreferredTaskDisplayArea;
}
- displayId = (displayId == INVALID_DISPLAY) ? currentParams.mPreferredDisplayId : displayId;
- return (displayId != INVALID_DISPLAY
- && mSupervisor.mRootWindowContainer.getDisplayContent(displayId) != null)
- ? displayId : DEFAULT_DISPLAY;
+ // Fallback to default display if the device didn't declare support for multi-display
+ if (taskDisplayArea != null && !mSupervisor.mService.mSupportsMultiDisplay
+ && taskDisplayArea.getDisplayId() != DEFAULT_DISPLAY) {
+ taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+ }
+
+ return (taskDisplayArea != null)
+ ? taskDisplayArea
+ : mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
}
private boolean canInheritWindowingModeFromSource(@NonNull DisplayContent display,
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 8b27667..160978d 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -186,12 +186,7 @@
return syncId;
}
- private int sanitizeAndApplyChange(WindowContainer container,
- WindowContainerTransaction.Change change) {
- if (!(container instanceof Task)) {
- throw new RuntimeException("Invalid token in task transaction");
- }
- final Task task = (Task) container;
+ private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) {
// The "client"-facing API should prevent bad changes; however, just in case, sanitize
// masks here.
final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
@@ -211,11 +206,38 @@
effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
}
- if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
- if (task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, change.getHidden())) {
- effects |= TRANSACT_EFFECTS_LIFECYCLE;
+
+ final int windowingMode = change.getWindowingMode();
+ if (windowingMode > -1) {
+ container.setWindowingMode(windowingMode);
+ }
+ return effects;
+ }
+
+ private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
+ int effects = 0;
+ final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
+
+ if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
+ if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
+ effects = TRANSACT_EFFECTS_LIFECYCLE;
}
}
+
+ final int childWindowingMode = c.getActivityWindowingMode();
+ if (childWindowingMode > -1) {
+ tr.setActivityWindowingMode(childWindowingMode);
+ }
+
+ if (t != null) {
+ tr.setMainWindowSizeChangeTransaction(t);
+ }
+
+ Rect enterPipBounds = c.getEnterPipBounds();
+ if (enterPipBounds != null) {
+ mService.mStackSupervisor.updatePictureInPictureMode(tr, enterPipBounds, true);
+ }
+
return effects;
}
@@ -283,30 +305,20 @@
return TRANSACT_EFFECTS_LIFECYCLE;
}
+ private void sanitizeWindowContainer(WindowContainer wc) {
+ if (!(wc instanceof Task) && !(wc instanceof DisplayArea)) {
+ throw new RuntimeException("Invalid token in task or displayArea transaction");
+ }
+ }
+
private int applyWindowContainerChange(WindowContainer wc,
WindowContainerTransaction.Change c) {
- int effects = sanitizeAndApplyChange(wc, c);
+ sanitizeWindowContainer(wc);
- final Task tr = wc.asTask();
+ int effects = applyChanges(wc, c);
- final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
- if (t != null) {
- tr.setMainWindowSizeChangeTransaction(t);
- }
-
- Rect enterPipBounds = c.getEnterPipBounds();
- if (enterPipBounds != null) {
- mService.mStackSupervisor.updatePictureInPictureMode(tr,
- enterPipBounds, true);
- }
-
- final int windowingMode = c.getWindowingMode();
- if (windowingMode > -1) {
- tr.setWindowingMode(windowingMode);
- }
- final int childWindowingMode = c.getActivityWindowingMode();
- if (childWindowingMode > -1) {
- tr.setActivityWindowingMode(childWindowingMode);
+ if (wc instanceof Task) {
+ effects |= applyTaskChanges(wc.asTask(), c);
}
return effects;
diff --git a/services/tests/servicestests/src/com/android/server/om/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/om/TEST_MAPPING
new file mode 100644
index 0000000..8070bd1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/om/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.om."
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index fcbd507..965304f 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -32,6 +32,7 @@
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
+import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -49,6 +50,7 @@
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.Map;
/**
@@ -96,13 +98,6 @@
}
};
- private static int getResourceIdFromHandle(int resourceHandle) {
- if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- return resourceHandle;
- }
- return (resourceHandle & 0x00ff0000) >> 16;
- }
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -247,7 +242,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(getResourceIdFromHandle(frontendHandle[0]))
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
.isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
}
@@ -275,7 +270,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(getResourceIdFromHandle(frontendHandle[0]))
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
.isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
}
@@ -307,7 +302,8 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(0);
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(0);
}
@Test
@@ -344,7 +340,8 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(infos[0].getId());
request =
new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
@@ -354,7 +351,8 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[1].getId());
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(infos[1].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].getId())
@@ -464,7 +462,11 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(infos[0].getId());
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
+ .getInUseFrontendIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(infos[0].getId(), infos[1].getId())));
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
@@ -474,7 +476,8 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[1].getId());
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(infos[1].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
@@ -487,6 +490,143 @@
}
@Test
+ public void releaseFrontendTest_UnderTheSameExclusiveGroup() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[1];
+ profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
+ mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ infos[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendHandle = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ int frontendId = mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]);
+ assertThat(frontendId).isEqualTo(infos[0].getId());
+ assertThat(mTunerResourceManagerService
+ .getFrontendResource(infos[1].getId()).isInUse()).isTrue();
+
+ // Release frontend
+ mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
+ .getFrontendResource(frontendId));
+ assertThat(mTunerResourceManagerService
+ .getFrontendResource(frontendId).isInUse()).isFalse();
+ assertThat(mTunerResourceManagerService
+ .getFrontendResource(infos[1].getId()).isInUse()).isFalse();
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(clientId[0]).getInUseFrontendIds().size()).isEqualTo(0);
+ }
+
+ @Test
+ public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[2];
+ profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientPriorities = {100, 500};
+ int[] clientId0 = new int[1];
+ int[] clientId1 = new int[1];
+ TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[0], listener, clientId0);
+ assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfile(clientId0[0])
+ .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[1], new TestResourcesReclaimListener(), clientId1);
+ assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfile(clientId1[0])
+ .setPriority(clientPriorities[1]);
+
+ // Init lnb resources.
+ int[] lnbIds = {1};
+ mTunerResourceManagerService.setLnbInfoListInternal(lnbIds);
+
+ TunerLnbRequest request = new TunerLnbRequest(clientId0[0]);
+ int[] lnbHandle = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService
+ .requestLnbInternal(request, lnbHandle)).isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]))
+ .isEqualTo(lnbIds[0]);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
+ .getInUseLnbIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(lnbIds[0])));
+
+ request = new TunerLnbRequest(clientId1[0]);
+ try {
+ assertThat(mTunerResourceManagerService
+ .requestLnbInternal(request, lnbHandle)).isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]))
+ .isEqualTo(lnbIds[0]);
+ assertThat(mTunerResourceManagerService.getLnbResource(lnbIds[0])
+ .isInUse()).isTrue();
+ assertThat(mTunerResourceManagerService.getLnbResource(lnbIds[0])
+ .getOwnerClientId()).isEqualTo(clientId1[0]);
+ assertThat(listener.isRelaimed()).isTrue();
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
+ .getInUseLnbIds().size()).isEqualTo(0);
+ }
+
+ @Test
+ public void releaseLnbTest() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[1];
+ profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
+ mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init lnb resources.
+ int[] lnbIds = {0};
+ mTunerResourceManagerService.setLnbInfoListInternal(lnbIds);
+
+ TunerLnbRequest request = new TunerLnbRequest(clientId[0]);
+ int[] lnbHandle = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService
+ .requestLnbInternal(request, lnbHandle)).isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ int lnbId = mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]);
+ assertThat(lnbId).isEqualTo(lnbIds[0]);
+
+ // Release lnb
+ mTunerResourceManagerService.releaseLnbInternal(mTunerResourceManagerService
+ .getLnbResource(lnbId));
+ assertThat(mTunerResourceManagerService
+ .getLnbResource(lnbId).isInUse()).isFalse();
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(clientId[0]).getInUseLnbIds().size()).isEqualTo(0);
+ }
+
+ @Test
public void unregisterClientTest_usingFrontend() {
// Register client
ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
@@ -513,7 +653,8 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(infos[0].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
@@ -543,7 +684,8 @@
TunerDemuxRequest request = new TunerDemuxRequest(clientId[0]);
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle))
.isTrue();
- assertThat(getResourceIdFromHandle(demuxHandle[0])).isEqualTo(0);
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(demuxHandle[0]))
+ .isEqualTo(0);
}
@Test
@@ -560,6 +702,6 @@
TunerDescramblerRequest request = new TunerDescramblerRequest(clientId[0]);
assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle))
.isTrue();
- assertThat(getResourceIdFromHandle(desHandle[0])).isEqualTo(0);
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(desHandle[0])).isEqualTo(0);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 97734ff..a84a0a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -426,7 +426,7 @@
// Start activity and delivered new intent.
starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
- doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
+ doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any());
final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
// Ensure result is delivering intent to top.
@@ -462,7 +462,7 @@
// Start activity and delivered new intent.
starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
- doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
+ doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any());
final int result = starter.setReason("testSplitScreenMoveToFront").execute();
// Ensure result is moving task to front.
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index 8a9504d..61de7d8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -19,7 +19,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -112,7 +111,7 @@
final ActivityRecord activity = new ActivityBuilder(mService).setComponent(name)
.setUid(userId).build();
final LaunchParams expected = new LaunchParams();
- expected.mPreferredDisplayId = 3;
+ expected.mPreferredTaskDisplayArea = mock(TaskDisplayArea.class);
expected.mWindowingMode = WINDOWING_MODE_PINNED;
expected.mBounds.set(200, 300, 400, 500);
@@ -183,7 +182,7 @@
final LaunchParams params = new LaunchParams();
params.mWindowingMode = WINDOWING_MODE_FREEFORM;
params.mBounds.set(0, 0, 30, 20);
- params.mPreferredDisplayId = 3;
+ params.mPreferredTaskDisplayArea = mock(TaskDisplayArea.class);
final InstrumentedPositioner positioner2 = new InstrumentedPositioner(RESULT_CONTINUE,
params);
@@ -228,8 +227,8 @@
*/
@Test
public void testVrPreferredDisplay() {
- final int vr2dDisplayId = 1;
- mService.mVr2dDisplayId = vr2dDisplayId;
+ final TestDisplayContent vrDisplay = createNewDisplayContent();
+ mService.mVr2dDisplayId = vrDisplay.mDisplayId;
final LaunchParams result = new LaunchParams();
final ActivityRecord vrActivity = new ActivityBuilder(mService).build();
@@ -238,16 +237,17 @@
// VR activities should always land on default display.
mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/,
null /*source*/, null /*options*/, PHASE_BOUNDS, result);
- assertEquals(DEFAULT_DISPLAY, result.mPreferredDisplayId);
+ assertEquals(mRootWindowContainer.getDefaultTaskDisplayArea(),
+ result.mPreferredTaskDisplayArea);
// Otherwise, always lands on VR 2D display.
final ActivityRecord vr2dActivity = new ActivityBuilder(mService).build();
mController.calculate(null /*task*/, null /*layout*/, vr2dActivity /*activity*/,
null /*source*/, null /*options*/, PHASE_BOUNDS, result);
- assertEquals(vr2dDisplayId, result.mPreferredDisplayId);
+ assertEquals(vrDisplay.getDefaultTaskDisplayArea(), result.mPreferredTaskDisplayArea);
mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
null /*options*/, PHASE_BOUNDS, result);
- assertEquals(vr2dDisplayId, result.mPreferredDisplayId);
+ assertEquals(vrDisplay.getDefaultTaskDisplayArea(), result.mPreferredTaskDisplayArea);
mService.mVr2dDisplayId = INVALID_DISPLAY;
}
@@ -282,9 +282,7 @@
final LaunchParams params = new LaunchParams();
final TestDisplayContent display = createNewDisplayContent();
final TaskDisplayArea preferredTaskDisplayArea = display.getDefaultTaskDisplayArea();
- // TODO(b/152116619): Enable after complete switch to WindowContainerToken
- //params.mPreferredWindowContainerToken = preferredTaskDisplayAreaToken;
- params.mPreferredDisplayId = display.mDisplayId;
+ params.mPreferredTaskDisplayArea = preferredTaskDisplayArea;
final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
final Task task = new TaskBuilder(mService.mStackSupervisor).build();
@@ -433,7 +431,7 @@
void saveTask(Task task, DisplayContent display) {
final int userId = task.mUserId;
final ComponentName realActivity = task.realActivity;
- mTmpParams.mPreferredDisplayId = task.getDisplayId();
+ mTmpParams.mPreferredTaskDisplayArea = task.getDisplayArea();
mTmpParams.mWindowingMode = task.getWindowingMode();
if (task.mLastNonFullscreenBounds != null) {
mTmpParams.mBounds.set(task.mLastNonFullscreenBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 6a71a7d..9bf86d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -19,7 +19,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.view.Display.INVALID_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -27,6 +26,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
@@ -163,7 +163,7 @@
mTarget.getLaunchParams(mTestTask, null, mResult);
- assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(mTestDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@@ -177,7 +177,7 @@
mTarget.getLaunchParams(null, activity, mResult);
- assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(mTestDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@@ -190,7 +190,7 @@
mTarget.getLaunchParams(mTestTask, null, mResult);
- assertEquals(INVALID_DISPLAY, mResult.mPreferredDisplayId);
+ assertNull(mResult.mPreferredTaskDisplayArea);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@@ -223,7 +223,7 @@
mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
- assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(mTestDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@@ -241,7 +241,7 @@
mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
- assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(mTestDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@@ -282,7 +282,7 @@
target.getLaunchParams(mTestTask, null, mResult);
- assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(mTestDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@@ -301,7 +301,7 @@
mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
target.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
- assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(mTestDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@@ -328,7 +328,7 @@
target.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
- assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(mTestDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 3c90515..5dba0045 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -537,8 +537,8 @@
doReturn(true).when(mRootWindowContainer)
.ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
- any(), anyInt(), anyBoolean());
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+ anyBoolean());
mRootWindowContainer.startHomeOnAllDisplays(0, "testStartHome");
@@ -578,17 +578,19 @@
// Can not start home if we don't want to start home while home is being instrumented.
doReturn(true).when(app).isInstrumenting();
- assertFalse(mRootWindowContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
+ .getDefaultTaskDisplayArea();
+ assertFalse(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
false /* allowInstrumenting*/));
// Can start home for other cases.
- assertTrue(mRootWindowContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
true /* allowInstrumenting*/));
doReturn(false).when(app).isInstrumenting();
- assertTrue(mRootWindowContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
false /* allowInstrumenting*/));
- assertTrue(mRootWindowContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
true /* allowInstrumenting*/));
}
@@ -694,8 +696,8 @@
resolutions.add(resolveInfo);
doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(),
refEq(secondaryHomeIntent));
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
- any(), anyInt(), anyBoolean());
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+ anyBoolean());
// Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
@@ -747,8 +749,8 @@
resolutions.add(infoFake2);
doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
- any(), anyInt(), anyBoolean());
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+ anyBoolean());
// Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
@@ -781,8 +783,8 @@
resolutions.add(infoFake2);
doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
- any(), anyInt(), anyBoolean());
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+ anyBoolean());
// Use the first one of matched activities in the same package as selected primary home.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
@@ -836,8 +838,9 @@
.setTask(task).build();
// Make sure the root task is valid and can be reused on default display.
- final ActivityStack stack = mRootWindowContainer.getValidLaunchStackOnDisplay(
- DEFAULT_DISPLAY, activity, task, null, null);
+ final ActivityStack stack = mRootWindowContainer.getValidLaunchStackInTaskDisplayArea(
+ mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, null,
+ null);
assertEquals(task, stack);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 1a38ff2..a69231b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -30,6 +30,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
@@ -44,7 +45,6 @@
import android.graphics.Rect;
import android.os.Build;
import android.platform.test.annotations.Presubmit;
-import android.view.Display;
import android.view.Gravity;
import androidx.test.filters.SmallTest;
@@ -106,53 +106,45 @@
// Display ID Related Tests
// =============================
@Test
- public void testDefaultToPrimaryDisplay() {
+ public void testDefaultToPrimaryDisplayArea() {
createNewDisplayContent(WINDOWING_MODE_FREEFORM);
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(DEFAULT_DISPLAY, mResult.mPreferredDisplayId);
+ assertEquals(mRootWindowContainer.getDefaultTaskDisplayArea(),
+ mResult.mPreferredTaskDisplayArea);
}
@Test
- public void testUsesDefaultDisplayIfPreviousDisplayNotExists() {
- mCurrent.mPreferredDisplayId = 19;
-
- assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
- mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
-
- assertEquals(DEFAULT_DISPLAY, mResult.mPreferredDisplayId);
- }
-
- @Test
- public void testUsesPreviousDisplayIdIfSet() {
+ public void testUsesPreviousDisplayAreaIfSet() {
createNewDisplayContent(WINDOWING_MODE_FREEFORM);
final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
- mCurrent.mPreferredDisplayId = display.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = display.getDefaultTaskDisplayArea();
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(display.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(display.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
}
@Test
- public void testUsesSourcesDisplayIdIfSet() {
+ public void testUsesSourcesDisplayAreaIfSet() {
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
WINDOWING_MODE_FULLSCREEN);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
ActivityRecord source = createSourceActivity(fullscreenDisplay);
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, source, /* options */ null, mCurrent, mResult));
- assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(fullscreenDisplay.getDefaultTaskDisplayArea(),
+ mResult.mPreferredTaskDisplayArea);
}
@Test
@@ -162,7 +154,7 @@
final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
WINDOWING_MODE_FULLSCREEN);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
ActivityRecord source = createSourceActivity(freeformDisplay);
ActivityOptions options = ActivityOptions.makeBasic();
@@ -171,28 +163,51 @@
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, source, options, mCurrent, mResult));
- assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(fullscreenDisplay.getDefaultTaskDisplayArea(),
+ mResult.mPreferredTaskDisplayArea);
}
@Test
- public void testUsesTasksDisplayIdPriorToSourceIfSet() {
+ public void testUsesOptionsDisplayAreaTokenIfSet() {
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
WINDOWING_MODE_FULLSCREEN);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
+ ActivityRecord source = createSourceActivity(freeformDisplay);
+
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchTaskDisplayArea(fullscreenDisplay.getDefaultTaskDisplayArea()
+ .mRemoteToken.toWindowContainerToken());
+
+ assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+ mActivity, source, options, mCurrent, mResult));
+
+ assertEquals(fullscreenDisplay.getDefaultTaskDisplayArea(),
+ mResult.mPreferredTaskDisplayArea);
+ }
+
+ @Test
+ public void testUsesTasksDisplayAreaIdPriorToSourceIfSet() {
+ final TestDisplayContent freeformDisplay = createNewDisplayContent(
+ WINDOWING_MODE_FREEFORM);
+ final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
+ WINDOWING_MODE_FULLSCREEN);
+
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
ActivityRecord reusableActivity = createSourceActivity(fullscreenDisplay);
ActivityRecord source = createSourceActivity(freeformDisplay);
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(reusableActivity.getTask(),
/* layout */ null, mActivity, source, /* options */ null, mCurrent, mResult));
- assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(fullscreenDisplay.getDefaultTaskDisplayArea(),
+ mResult.mPreferredTaskDisplayArea);
}
@Test
- public void testUsesTaskDisplayIdIfSet() {
+ public void testUsesTaskDisplayAreaIdIfSet() {
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
ActivityRecord source = createSourceActivity(freeformDisplay);
@@ -200,7 +215,8 @@
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(source.getTask(), null /* layout */,
null /* activity */, null /* source */, null /* options */, mCurrent, mResult));
- assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(freeformDisplay.getDefaultTaskDisplayArea(),
+ mResult.mPreferredTaskDisplayArea);
}
@Test
@@ -210,7 +226,7 @@
final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
WINDOWING_MODE_FULLSCREEN);
- mCurrent.mPreferredDisplayId = fullscreenDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = fullscreenDisplay.getDefaultTaskDisplayArea();
ActivityRecord reusableActivity = createSourceActivity(fullscreenDisplay);
ActivityRecord source = createSourceActivity(freeformDisplay);
source.mHandoverLaunchDisplayId = freeformDisplay.mDisplayId;
@@ -219,7 +235,28 @@
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(reusableActivity.getTask(),
null /* layout */, mActivity, source, null /* options */, mCurrent, mResult));
- assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(freeformDisplay.getDefaultTaskDisplayArea(),
+ mResult.mPreferredTaskDisplayArea);
+ }
+
+ @Test
+ public void testUsesNoDisplaySourceHandoverDisplayAreaIdIfSet() {
+ final TestDisplayContent freeformDisplay = createNewDisplayContent(
+ WINDOWING_MODE_FREEFORM);
+ final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
+ WINDOWING_MODE_FULLSCREEN);
+
+ mCurrent.mPreferredTaskDisplayArea = fullscreenDisplay.getDefaultTaskDisplayArea();
+ ActivityRecord reusableActivity = createSourceActivity(fullscreenDisplay);
+ ActivityRecord source = createSourceActivity(freeformDisplay);
+ source.mHandoverTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
+ source.noDisplay = true;
+
+ assertEquals(RESULT_CONTINUE, mTarget.onCalculate(reusableActivity.getTask(),
+ null /* layout */, mActivity, source, null /* options */, mCurrent, mResult));
+
+ assertEquals(freeformDisplay.getDefaultTaskDisplayArea(),
+ mResult.mPreferredTaskDisplayArea);
}
// =====================================
@@ -233,7 +270,7 @@
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchBounds(new Rect(0, 0, 100, 100));
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
@@ -247,7 +284,7 @@
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchBounds(new Rect(0, 0, 100, 100));
- mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
+ mCurrent.mPreferredTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
@@ -278,7 +315,7 @@
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
@@ -296,7 +333,7 @@
options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
options.setLaunchBounds(new Rect(0, 0, 100, 100));
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
@@ -310,7 +347,7 @@
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
- mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
+ mCurrent.mPreferredTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
@@ -324,7 +361,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidth(120).setHeight(80).build();
@@ -341,7 +378,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setGravity(Gravity.LEFT).build();
@@ -358,7 +395,7 @@
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidth(120).setHeight(80).build();
- mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
+ mCurrent.mPreferredTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
@@ -369,7 +406,7 @@
@Test
public void testLaunchesFullscreenOnFullscreenDisplayWithFreeformHistory() {
- mCurrent.mPreferredDisplayId = Display.INVALID_DISPLAY;
+ mCurrent.mPreferredTaskDisplayArea = null;
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(0, 0, 200, 100);
@@ -385,7 +422,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
mCurrent.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
@@ -400,7 +437,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(0, 0, 200, 100);
@@ -421,7 +458,7 @@
options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
options.setLaunchBounds(new Rect(0, 0, 200, 100));
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(0, 0, 200, 100);
@@ -446,7 +483,7 @@
options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
options.setLaunchBounds(expectedLaunchBounds);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(expectedLaunchBounds);
@@ -467,7 +504,7 @@
options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
options.setLaunchBounds(new Rect(0, 0, 200, 100));
- mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
+ mCurrent.mPreferredTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(0, 0, 200, 100);
@@ -568,7 +605,7 @@
final Rect expected = new Rect(0, 0, 100, 100);
options.setLaunchBounds(expected);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
@@ -598,7 +635,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setGravity(Gravity.LEFT).build();
@@ -614,7 +651,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setGravity(Gravity.TOP).build();
@@ -630,7 +667,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setGravity(Gravity.TOP | Gravity.LEFT).build();
@@ -647,7 +684,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setGravity(Gravity.RIGHT).build();
@@ -663,7 +700,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setGravity(Gravity.BOTTOM).build();
@@ -679,7 +716,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setGravity(Gravity.BOTTOM | Gravity.RIGHT).build();
@@ -696,7 +733,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidth(120).setHeight(80).build();
@@ -712,7 +749,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidth(120).setHeight(80).setGravity(Gravity.LEFT).build();
@@ -728,7 +765,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidth(120).setHeight(80).setGravity(Gravity.TOP).build();
@@ -744,7 +781,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidth(120).setHeight(80).setGravity(Gravity.TOP | Gravity.LEFT).build();
@@ -760,7 +797,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidth(120).setHeight(80).setGravity(Gravity.RIGHT).build();
@@ -776,7 +813,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidth(120).setHeight(80).setGravity(Gravity.BOTTOM).build();
@@ -792,7 +829,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidth(120).setHeight(80).setGravity(Gravity.BOTTOM | Gravity.RIGHT).build();
@@ -808,7 +845,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidthFraction(0.125f).setHeightFraction(0.1f).build();
@@ -824,7 +861,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(0, 0, 200, 100);
@@ -839,7 +876,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
mCurrent.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
mCurrent.mBounds.set(0, 0, 200, 100);
@@ -1217,7 +1254,7 @@
@Test
public void returnsNonFullscreenBoundsOnFullscreenDisplayWithFreeformHistory() {
- mCurrent.mPreferredDisplayId = Display.INVALID_DISPLAY;
+ mCurrent.mPreferredTaskDisplayArea = null;
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(0, 0, 200, 100);
@@ -1233,7 +1270,7 @@
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- mCurrent.mPreferredDisplayId = Display.INVALID_DISPLAY;
+ mCurrent.mPreferredTaskDisplayArea = null;
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(-100, -200, 200, 100);
@@ -1253,7 +1290,7 @@
addFreeformTaskTo(freeformDisplay, new Rect(0, 0, 200, 100));
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(0, 0, 200, 100);
@@ -1284,13 +1321,14 @@
public void testNoMultiDisplaySupports() {
final boolean orgValue = mService.mSupportsMultiDisplay;
final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
- mCurrent.mPreferredDisplayId = display.mDisplayId;
+ mCurrent.mPreferredTaskDisplayArea = display.getDefaultTaskDisplayArea();
try {
mService.mSupportsMultiDisplay = false;
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(DEFAULT_DISPLAY, mResult.mPreferredDisplayId);
+ assertEquals(mRootWindowContainer.getDefaultTaskDisplayArea(),
+ mResult.mPreferredTaskDisplayArea);
} finally {
mService.mSupportsMultiDisplay = orgValue;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
similarity index 94%
rename from services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index e41d4dc..53cc09b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -39,6 +39,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
@@ -70,7 +71,6 @@
import androidx.test.filters.SmallTest;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -87,7 +87,7 @@
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
-public class TaskOrganizerTests extends WindowTestsBase {
+public class WindowOrganizerTests extends WindowTestsBase {
private ITaskOrganizer registerMockOrganizer(int windowingMode) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
when(organizer.asBinder()).thenReturn(new Binder());
@@ -307,11 +307,7 @@
final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
final Task task = stack.getTopMostTask();
- WindowContainerTransaction t = new WindowContainerTransaction();
- Rect newBounds = new Rect(10, 10, 100, 100);
- t.setBounds(task.mRemoteToken.toWindowContainerToken(), new Rect(10, 10, 100, 100));
- mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
- assertEquals(newBounds, task.getBounds());
+ testTransaction(task);
}
@Test
@@ -321,24 +317,41 @@
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
StackInfo info =
mWm.mAtmService.getStackInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
- WindowContainerTransaction t = new WindowContainerTransaction();
assertEquals(stack.mRemoteToken.toWindowContainerToken(), info.stackToken);
+ testTransaction(stack);
+ }
+
+ @Test
+ public void testDisplayAreaTransaction() {
+ removeGlobalMinSizeRestriction();
+ final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea");
+ testTransaction(displayArea);
+ }
+
+ private void testTransaction(WindowContainer wc) {
+ WindowContainerTransaction t = new WindowContainerTransaction();
Rect newBounds = new Rect(10, 10, 100, 100);
- t.setBounds(info.stackToken, new Rect(10, 10, 100, 100));
+ t.setBounds(wc.mRemoteToken.toWindowContainerToken(), new Rect(10, 10, 100, 100));
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
- assertEquals(newBounds, stack.getBounds());
+ assertEquals(newBounds, wc.getBounds());
}
@Test
public void testSetWindowingMode() {
final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
- .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ testSetWindowingMode(stack);
+
+ final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea");
+ displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ testSetWindowingMode(displayArea);
+ }
+
+ private void testSetWindowingMode(WindowContainer wc) {
final WindowContainerTransaction t = new WindowContainerTransaction();
-
- t.setWindowingMode(stack.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN);
+ t.setWindowingMode(wc.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
-
- assertEquals(WINDOWING_MODE_FULLSCREEN, stack.getWindowingMode());
+ assertEquals(WINDOWING_MODE_FULLSCREEN, wc.getWindowingMode());
}
@Test
@@ -400,7 +413,8 @@
final int origScreenHDp = task.getConfiguration().screenHeightDp;
t = new WindowContainerTransaction();
// verify that setting config overrides on parent restricts children.
- t.setScreenSizeDp(stack.mRemoteToken.toWindowContainerToken(), origScreenWDp, origScreenHDp);
+ t.setScreenSizeDp(stack.mRemoteToken
+ .toWindowContainerToken(), origScreenWDp, origScreenHDp);
t.setBounds(task.mRemoteToken.toWindowContainerToken(), new Rect(10, 10, 150, 200));
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp);
@@ -665,7 +679,7 @@
BLASTSyncEngine bse = new BLASTSyncEngine();
BLASTSyncEngine.TransactionReadyListener transactionListener =
- mock(BLASTSyncEngine.TransactionReadyListener.class);
+ mock(BLASTSyncEngine.TransactionReadyListener.class);
int id = bse.startSyncSet(transactionListener);
bse.addToSyncSet(id, task);
@@ -689,7 +703,7 @@
BLASTSyncEngine bse = new BLASTSyncEngine();
BLASTSyncEngine.TransactionReadyListener transactionListener =
- mock(BLASTSyncEngine.TransactionReadyListener.class);
+ mock(BLASTSyncEngine.TransactionReadyListener.class);
int id = bse.startSyncSet(transactionListener);
assertEquals(true, bse.addToSyncSet(id, task));
@@ -715,7 +729,7 @@
BLASTSyncEngine bse = new BLASTSyncEngine();
BLASTSyncEngine.TransactionReadyListener transactionListener =
- mock(BLASTSyncEngine.TransactionReadyListener.class);
+ mock(BLASTSyncEngine.TransactionReadyListener.class);
int id = bse.startSyncSet(transactionListener);
bse.addToSyncSet(id, task);
@@ -738,7 +752,7 @@
BLASTSyncEngine bse = new BLASTSyncEngine();
BLASTSyncEngine.TransactionReadyListener transactionListener =
- mock(BLASTSyncEngine.TransactionReadyListener.class);
+ mock(BLASTSyncEngine.TransactionReadyListener.class);
int id = bse.startSyncSet(transactionListener);
bse.addToSyncSet(id, task);
@@ -764,7 +778,7 @@
BLASTSyncEngine bse = new BLASTSyncEngine();
BLASTSyncEngine.TransactionReadyListener transactionListener =
- mock(BLASTSyncEngine.TransactionReadyListener.class);
+ mock(BLASTSyncEngine.TransactionReadyListener.class);
int id = bse.startSyncSet(transactionListener);
assertEquals(true, bse.addToSyncSet(id, task));
@@ -817,8 +831,8 @@
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o, WINDOWING_MODE_PINNED);
final ActivityRecord record = makePipableActivity();
- final PictureInPictureParams p =
- new PictureInPictureParams.Builder().setAspectRatio(new Rational(1, 2)).build();
+ final PictureInPictureParams p = new PictureInPictureParams.Builder()
+ .setAspectRatio(new Rational(1, 2)).build();
assertTrue(mWm.mAtmService.enterPictureInPictureMode(record.token, p));
waitUntilHandlersIdle();
assertNotNull(o.mInfo);
@@ -838,15 +852,15 @@
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o, WINDOWING_MODE_PINNED);
final ActivityRecord record = makePipableActivity();
- final PictureInPictureParams p =
- new PictureInPictureParams.Builder().setAspectRatio(new Rational(1, 2)).build();
+ final PictureInPictureParams p = new PictureInPictureParams.Builder()
+ .setAspectRatio(new Rational(1, 2)).build();
assertTrue(mWm.mAtmService.enterPictureInPictureMode(record.token, p));
waitUntilHandlersIdle();
assertNotNull(o.mInfo);
assertNotNull(o.mInfo.pictureInPictureParams);
- final PictureInPictureParams p2 =
- new PictureInPictureParams.Builder().setAspectRatio(new Rational(3, 4)).build();
+ final PictureInPictureParams p2 = new PictureInPictureParams.Builder()
+ .setAspectRatio(new Rational(3, 4)).build();
mWm.mAtmService.setPictureInPictureParams(record.token, p2);
waitUntilHandlersIdle();
assertNotNull(o.mChangedInfo);
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
new file mode 100644
index 0000000..4601c4b
--- /dev/null
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net
+
+import android.app.Instrumentation
+import android.content.Context
+import android.net.NetworkCapabilities.TRANSPORT_TEST
+import android.os.Build
+import android.os.HandlerThread
+import android.os.Looper
+import android.net.NetworkProviderTest.TestNetworkCallback.CallbackEntry.OnUnavailable
+import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequested
+import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequestWithdrawn
+import androidx.test.InstrumentationRegistry
+import com.android.testutils.ArrayTrackRecord
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import java.util.UUID
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val DEFAULT_TIMEOUT_MS = 5000L
+private val instrumentation: Instrumentation
+ get() = InstrumentationRegistry.getInstrumentation()
+private val context: Context get() = InstrumentationRegistry.getContext()
+private val PROVIDER_NAME = "NetworkProviderTest"
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.Q)
+class NetworkProviderTest {
+ private val mCm = context.getSystemService(ConnectivityManager::class.java)
+ private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
+
+ @Before
+ fun setUp() {
+ instrumentation.getUiAutomation().adoptShellPermissionIdentity()
+ mHandlerThread.start()
+ }
+
+ @After
+ fun tearDown() {
+ mHandlerThread.quitSafely()
+ instrumentation.getUiAutomation().dropShellPermissionIdentity()
+ }
+
+ private class TestNetworkProvider(context: Context, looper: Looper) :
+ NetworkProvider(context, looper, PROVIDER_NAME) {
+ private val seenEvents = ArrayTrackRecord<CallbackEntry>().newReadHead()
+
+ sealed class CallbackEntry {
+ data class OnNetworkRequested(
+ val request: NetworkRequest,
+ val score: Int,
+ val id: Int
+ ) : CallbackEntry()
+ data class OnNetworkRequestWithdrawn(val request: NetworkRequest) : CallbackEntry()
+ }
+
+ override fun onNetworkRequested(request: NetworkRequest, score: Int, id: Int) {
+ seenEvents.add(OnNetworkRequested(request, score, id))
+ }
+
+ override fun onNetworkRequestWithdrawn(request: NetworkRequest) {
+ seenEvents.add(OnNetworkRequestWithdrawn(request))
+ }
+
+ inline fun <reified T : CallbackEntry> expectCallback(
+ crossinline predicate: (T) -> Boolean
+ ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) }
+ }
+
+ private fun createNetworkProvider(): TestNetworkProvider {
+ return TestNetworkProvider(context, mHandlerThread.looper)
+ }
+
+ @Test
+ fun testOnNetworkRequested() {
+ val provider = createNetworkProvider()
+ assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
+ mCm.registerNetworkProvider(provider)
+ assertNotEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
+
+ val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
+ val nr: NetworkRequest = NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_TEST)
+ .setNetworkSpecifier(specifier)
+ .build()
+ val cb = ConnectivityManager.NetworkCallback()
+ mCm.requestNetwork(nr, cb)
+ provider.expectCallback<OnNetworkRequested>() {
+ callback -> callback.request.getNetworkSpecifier() == specifier &&
+ callback.request.hasTransport(TRANSPORT_TEST)
+ }
+
+ mCm.unregisterNetworkCallback(cb)
+ provider.expectCallback<OnNetworkRequestWithdrawn>() {
+ callback -> callback.request.getNetworkSpecifier() == specifier &&
+ callback.request.hasTransport(TRANSPORT_TEST)
+ }
+ mCm.unregisterNetworkProvider(provider)
+ // Provider id should be ID_NONE after unregister network provider
+ assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
+ // unregisterNetworkProvider should not crash even if it's called on an
+ // already unregistered provider.
+ mCm.unregisterNetworkProvider(provider)
+ }
+
+ private class TestNetworkCallback : ConnectivityManager.NetworkCallback() {
+ private val seenEvents = ArrayTrackRecord<CallbackEntry>().newReadHead()
+ sealed class CallbackEntry {
+ object OnUnavailable : CallbackEntry()
+ }
+
+ override fun onUnavailable() {
+ seenEvents.add(OnUnavailable)
+ }
+
+ inline fun <reified T : CallbackEntry> expectCallback(
+ crossinline predicate: (T) -> Boolean
+ ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) }
+ }
+
+ @Test
+ fun testDeclareNetworkRequestUnfulfillable() {
+ val provider = createNetworkProvider()
+ mCm.registerNetworkProvider(provider)
+
+ val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
+ val nr: NetworkRequest = NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_TEST)
+ .setNetworkSpecifier(specifier)
+ .build()
+
+ val cb = TestNetworkCallback()
+ mCm.requestNetwork(nr, cb)
+ provider.declareNetworkRequestUnfulfillable(nr)
+ cb.expectCallback<OnUnavailable>() { nr.getNetworkSpecifier() == specifier }
+ mCm.unregisterNetworkProvider(provider)
+ }
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b864e37..dad0363 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5969,6 +5969,9 @@
final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64");
final String kNat64PrefixString = "2001:db8:64:64:64:64::";
final IpPrefix kNat64Prefix = new IpPrefix(InetAddress.getByName(kNat64PrefixString), 96);
+ final String kOtherNat64PrefixString = "64:ff9b::";
+ final IpPrefix kOtherNat64Prefix = new IpPrefix(
+ InetAddress.getByName(kOtherNat64PrefixString), 96);
final RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, myIpv6.getAddress(),
MOBILE_IFNAME);
final RouteInfo ipv6Subnet = new RouteInfo(myIpv6, null, MOBILE_IFNAME);
@@ -6082,6 +6085,24 @@
}
reset(mMockNetd);
+ // Change the NAT64 prefix without first removing it.
+ // Expect clatd to be stopped and started with the new prefix.
+ mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
+ kOtherNat64PrefixString, 96);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getStackedLinks().size() == 0);
+ verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+ assertRoutesRemoved(cellNetId, stackedDefault);
+
+ verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kOtherNat64Prefix.toString());
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getNat64Prefix().equals(kOtherNat64Prefix));
+ clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getStackedLinks().size() == 1);
+ assertRoutesAdded(cellNetId, stackedDefault);
+ reset(mMockNetd);
+
// Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
// linkproperties are cleaned up.
cellLp.addLinkAddress(myIpv4);
@@ -6096,7 +6117,7 @@
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
LinkProperties expected = new LinkProperties(cellLp);
- expected.setNat64Prefix(kNat64Prefix);
+ expected.setNat64Prefix(kOtherNat64Prefix);
assertEquals(expected, actualLpAfterIpv4);
assertEquals(0, actualLpAfterIpv4.getStackedLinks().size());
assertRoutesRemoved(cellNetId, stackedDefault);
@@ -6115,7 +6136,7 @@
// Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
- kNat64PrefixString, 96);
+ kOtherNat64PrefixString, 96);
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getNat64Prefix() == null);
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index a392ae3..0a603b8 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -18,6 +18,8 @@
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.NetworkCapabilities.MAX_TRANSPORT;
+import static android.net.NetworkCapabilities.MIN_TRANSPORT;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
@@ -30,6 +32,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.reset;
@@ -44,16 +47,19 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.ResolverOptionsParcel;
import android.net.ResolverParamsParcel;
import android.net.RouteInfo;
import android.net.shared.PrivateDnsConfig;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
+import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.MessageUtils;
import com.android.internal.util.test.FakeSettingsProvider;
import org.junit.Before;
@@ -354,4 +360,23 @@
expectedParams.resolverOptions = new ResolverOptionsParcel();
assertResolverParamsEquals(actualParams, expectedParams);
}
+
+ @Test
+ public void testTransportTypesEqual() throws Exception {
+ SparseArray<String> ncTransTypes = MessageUtils.findMessageNames(
+ new Class[] { NetworkCapabilities.class }, new String[]{ "TRANSPORT_" });
+ SparseArray<String> dnsTransTypes = MessageUtils.findMessageNames(
+ new Class[] { IDnsResolver.class }, new String[]{ "TRANSPORT_" });
+ assertEquals(0, MIN_TRANSPORT);
+ assertEquals(MAX_TRANSPORT + 1, ncTransTypes.size());
+ // TRANSPORT_UNKNOWN in IDnsResolver is defined to -1 and only for resolver.
+ assertEquals("TRANSPORT_UNKNOWN", dnsTransTypes.get(-1));
+ assertEquals(ncTransTypes.size(), dnsTransTypes.size() - 1);
+ for (int i = MIN_TRANSPORT; i < MAX_TRANSPORT; i++) {
+ String name = ncTransTypes.get(i, null);
+ assertNotNull("Could not find NetworkCapabilies.TRANSPORT_* constant equal to "
+ + i, name);
+ assertEquals(name, dnsTransTypes.get(i));
+ }
+ }
}
diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp
index d1a86c2..ce551bd 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -6,7 +6,7 @@
static_libs: [
"protolog-common",
"javaparser",
- "protolog-proto",
+ "platformprotos",
"jsonlib",
],
}