Merge "Make TextAppearance.DeviceDefault.Headline public."
diff --git a/Android.bp b/Android.bp
index cf3c017..d64951c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -172,6 +172,7 @@
"framework-supplementalprocess.stubs.module_lib",
"framework-tethering.stubs.module_lib",
"framework-uwb.stubs.module_lib",
+ "framework-nearby.stubs.module_lib",
"framework-wifi.stubs.module_lib",
],
soong_config_variables: {
@@ -203,6 +204,7 @@
"framework-statsd.impl",
"framework-supplementalprocess.impl",
"framework-tethering.impl",
+ "framework-nearby.impl",
"framework-uwb.impl",
"framework-wifi.impl",
"updatable-media",
diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS
index fbc611a..9d92e0f 100644
--- a/MULTIUSER_OWNERS
+++ b/MULTIUSER_OWNERS
@@ -1,4 +1,5 @@
# OWNERS of Multiuser related files
bookatz@google.com
+olilan@google.com
omakoto@google.com
yamasani@google.com
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 3b38860..1e16f94 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -67,19 +67,13 @@
},
dists: [
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/public/api",
dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/public/api",
dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
@@ -123,19 +117,13 @@
},
dists: [
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/system/api",
dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/system/api",
dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
@@ -159,37 +147,25 @@
},
dists: [
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/test/api",
dest: "android.txt",
tag: ".api.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/test/api",
dest: "removed.txt",
tag: ".removed-api.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/test/api",
dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/test/api",
dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
@@ -218,19 +194,13 @@
},
dists: [
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/module-lib/api",
dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/module-lib/api",
dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
@@ -298,10 +268,7 @@
java_version: "1.8",
compile_dex: true,
dist: {
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
tag: ".jar",
dest: "android-non-updatable.jar",
},
@@ -378,10 +345,7 @@
java_defaults {
name: "android_stubs_dists_default",
dist: {
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
tag: ".jar",
dest: "android.jar",
},
@@ -427,10 +391,7 @@
dists: [
{
// Legacy dist path
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
tag: ".jar",
dest: "android_system.jar",
},
@@ -470,6 +431,7 @@
static_libs: [
"android-non-updatable.stubs.module_lib",
"art.module.public.api.stubs.module_lib",
+ "i18n.module.public.api.stubs",
],
dist: {
dir: "apistubs/android/module-lib",
diff --git a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
new file mode 100644
index 0000000..cf94e9e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2022 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.view;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.TOOL_TYPE_FINGER;
+import static android.view.MotionEvent.TOOL_TYPE_STYLUS;
+
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.Rect;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Benchmark tests for {@link HandwritingInitiator}
+ *
+ * Build/Install/Run:
+ * atest CorePerfTests:android.view.HandwritingInitiatorPerfTest
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class HandwritingInitiatorPerfTest {
+ private Context mContext;
+ private HandwritingInitiator mHandwritingInitiator;
+ private int mTouchSlop;
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Before
+ public void setup() {
+ final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getTargetContext();
+ ViewConfiguration viewConfiguration = ViewConfiguration.get(mContext);
+ mTouchSlop = viewConfiguration.getScaledTouchSlop();
+ InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
+ mHandwritingInitiator = new HandwritingInitiator(viewConfiguration, inputMethodManager);
+ }
+
+ @Test
+ public void onTouchEvent_actionDown_toolTypeStylus() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_STYLUS, 10, 10, 0);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_STYLUS, 11, 11, 1);
+
+ while (state.keepRunning()) {
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void onTouchEvent_actionUp_toolTypeStylus() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_STYLUS, 10, 10, 0);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_STYLUS, 11, 11, 1);
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ state.resumeTiming();
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ }
+ }
+
+ @Test
+ public void onTouchEvent_actionMove_toolTypeStylus() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final int initX = 10;
+ final int initY = 10;
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_STYLUS, initX, initY, 0);
+
+ final int x = initX + mTouchSlop;
+ final int y = initY + mTouchSlop;
+ final MotionEvent moveEvent =
+ createMotionEvent(ACTION_MOVE, TOOL_TYPE_STYLUS, x, y, 1);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_STYLUS, x, y, 1);
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ state.resumeTiming();
+
+ mHandwritingInitiator.onTouchEvent(moveEvent);
+
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void onTouchEvent_actionDown_toolTypeFinger() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_FINGER, 10, 10, 0);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_FINGER, 11, 11, 1);
+
+ while (state.keepRunning()) {
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ }
+ }
+
+ @Test
+ public void onTouchEvent_actionUp_toolTypeFinger() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_FINGER, 10, 10, 0);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_FINGER, 11, 11, 1);
+
+ while (state.keepRunning()) {
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void onTouchEvent_actionMove_toolTypeFinger() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final int initX = 10;
+ final int initY = 10;
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_FINGER, initX, initY, 0);
+
+ final int x = initX + mTouchSlop;
+ final int y = initY + mTouchSlop;
+ final MotionEvent moveEvent =
+ createMotionEvent(ACTION_MOVE, TOOL_TYPE_FINGER, x, y, 1);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_FINGER, x, y, 1);
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ state.resumeTiming();
+
+ mHandwritingInitiator.onTouchEvent(moveEvent);
+
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void onInputConnectionCreated() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final View view = new View(mContext);
+ final EditorInfo editorInfo = new EditorInfo();
+ while (state.keepRunning()) {
+ mHandwritingInitiator.onInputConnectionCreated(view, editorInfo);
+ state.pauseTiming();
+ mHandwritingInitiator.onInputConnectionClosed(view);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void onInputConnectionClosed() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final View view = new View(mContext);
+ final EditorInfo editorInfo = new EditorInfo();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ mHandwritingInitiator.onInputConnectionCreated(view, editorInfo);
+ state.resumeTiming();
+ mHandwritingInitiator.onInputConnectionClosed(view);
+ }
+ }
+
+ @Test
+ public void updateEditorBoundary() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Rect rect = new Rect(0, 0, 100, 100);
+ while (state.keepRunning()) {
+ mHandwritingInitiator.updateEditorBound(rect);
+ }
+ }
+
+ private MotionEvent createMotionEvent(int action, int toolType, int x, int y, long eventTime) {
+ MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
+ properties[0].toolType = toolType;
+
+ MotionEvent.PointerCoords[] coords = MotionEvent.PointerCoords.createArray(1);
+ coords[0].x = x;
+ coords[0].y = y;
+
+ return MotionEvent.obtain(0 /* downTime */, eventTime /* eventTime */, action, 1,
+ properties, coords, 0 /* metaState */, 0 /* buttonState */, 1 /* xPrecision */,
+ 1 /* yPrecision */, 0 /* deviceId */, 0 /* edgeFlags */,
+ InputDevice.SOURCE_TOUCHSCREEN, 0 /* flags */);
+ }
+}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index c251529a..e5b0742 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -125,7 +125,7 @@
* will not be invoked.
*
* @param params Parameters specifying info about this job, including the optional
- * extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle).
+ * extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle)}.
* This object serves to identify this specific running job instance when calling
* {@link #jobFinished(JobParameters, boolean)}.
* @return {@code true} if your service will continue running, using a separate thread
diff --git a/api/Android.bp b/api/Android.bp
index 51422b3..2e49a0c 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -124,10 +124,7 @@
dest: "current.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/public/api",
dest: "android.txt",
},
@@ -217,10 +214,7 @@
dest: "removed.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/public/api",
dest: "removed.txt",
},
@@ -260,10 +254,7 @@
dest: "system-current.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/system/api",
dest: "android.txt",
},
@@ -322,10 +313,7 @@
dest: "system-removed.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/system/api",
dest: "removed.txt",
},
@@ -366,10 +354,7 @@
dest: "module-lib-current.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/module-lib/api",
dest: "android.txt",
},
@@ -430,10 +415,7 @@
dest: "module-lib-removed.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/module-lib/api",
dest: "removed.txt",
},
@@ -474,10 +456,7 @@
dest: "system-server-current.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/system-server/api",
dest: "android.txt",
},
@@ -501,10 +480,7 @@
dest: "system-server-removed.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/system-server/api",
dest: "removed.txt",
},
@@ -546,9 +522,6 @@
tools: ["api_versions_trimmer"],
cmd: "$(location api_versions_trimmer) $(out) $(in)",
dist: {
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
},
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 05a06619..1f4a64f 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -81,18 +81,18 @@
static constexpr const char* OEM_USERSPACE_REBOOT_ANIMATION_FILE = "/oem/media/userspace-reboot.zip";
static constexpr const char* SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE = "/system/media/userspace-reboot.zip";
-static const char SYSTEM_DATA_DIR_PATH[] = "/data/system";
-static const char SYSTEM_TIME_DIR_NAME[] = "time";
-static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time";
+static const char BOOTANIM_DATA_DIR_PATH[] = "/data/bootanim";
+static const char BOOTANIM_TIME_DIR_NAME[] = "time";
+static const char BOOTANIM_TIME_DIR_PATH[] = "/data/bootanim/time";
static const char CLOCK_FONT_ASSET[] = "images/clock_font.png";
static const char CLOCK_FONT_ZIP_NAME[] = "clock_font.png";
static const char PROGRESS_FONT_ASSET[] = "images/progress_font.png";
static const char PROGRESS_FONT_ZIP_NAME[] = "progress_font.png";
static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change";
-static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
+static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/bootanim/time/last_time_change";
static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
-static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_accurate";
-static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/system/time/time_format_12_hour";
+static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/bootanim/time/time_is_accurate";
+static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/bootanim/time/time_format_12_hour";
// Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
static const long long ACCURATE_TIME_EPOCH = 946684800000;
static constexpr char FONT_BEGIN_CHAR = ' ';
@@ -1741,7 +1741,7 @@
}
BootAnimation::TimeCheckThread::TimeCheckThread(BootAnimation* bootAnimation) : Thread(false),
- mInotifyFd(-1), mSystemWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {}
+ mInotifyFd(-1), mBootAnimWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {}
BootAnimation::TimeCheckThread::~TimeCheckThread() {
// mInotifyFd may be -1 but that's ok since we're not at risk of attempting to close a valid FD.
@@ -1784,7 +1784,7 @@
const struct inotify_event *event;
for (char* ptr = buff; ptr < buff + length; ptr += sizeof(struct inotify_event) + event->len) {
event = (const struct inotify_event *) ptr;
- if (event->wd == mSystemWd && strcmp(SYSTEM_TIME_DIR_NAME, event->name) == 0) {
+ if (event->wd == mBootAnimWd && strcmp(BOOTANIM_TIME_DIR_NAME, event->name) == 0) {
addTimeDirWatch();
} else if (event->wd == mTimeWd && (strcmp(LAST_TIME_CHANGED_FILE_NAME, event->name) == 0
|| strcmp(ACCURATE_TIME_FLAG_FILE_NAME, event->name) == 0)) {
@@ -1796,12 +1796,12 @@
}
void BootAnimation::TimeCheckThread::addTimeDirWatch() {
- mTimeWd = inotify_add_watch(mInotifyFd, SYSTEM_TIME_DIR_PATH,
+ mTimeWd = inotify_add_watch(mInotifyFd, BOOTANIM_TIME_DIR_PATH,
IN_CLOSE_WRITE | IN_MOVED_TO | IN_ATTRIB);
if (mTimeWd > 0) {
// No need to watch for the time directory to be created if it already exists
- inotify_rm_watch(mInotifyFd, mSystemWd);
- mSystemWd = -1;
+ inotify_rm_watch(mInotifyFd, mBootAnimWd);
+ mBootAnimWd = -1;
}
}
@@ -1812,11 +1812,11 @@
return NO_INIT;
}
- mSystemWd = inotify_add_watch(mInotifyFd, SYSTEM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB);
- if (mSystemWd < 0) {
+ mBootAnimWd = inotify_add_watch(mInotifyFd, BOOTANIM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB);
+ if (mBootAnimWd < 0) {
close(mInotifyFd);
mInotifyFd = -1;
- SLOGE("Could not add watch for %s: %s", SYSTEM_DATA_DIR_PATH, strerror(errno));
+ SLOGE("Could not add watch for %s: %s", BOOTANIM_DATA_DIR_PATH, strerror(errno));
return NO_INIT;
}
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 7a597da..4c378cb 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -161,7 +161,7 @@
void addTimeDirWatch();
int mInotifyFd;
- int mSystemWd;
+ int mBootAnimWd;
int mTimeWd;
BootAnimation* mBootAnimation;
};
diff --git a/core/api/current.txt b/core/api/current.txt
index 8d7cd7c..70c09a8 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3015,6 +3015,8 @@
public abstract class AccessibilityService extends android.app.Service {
ctor public AccessibilityService();
+ method public boolean clearCache();
+ method public boolean clearCachedSubtree(@NonNull android.view.accessibility.AccessibilityNodeInfo);
method public final void disableSelf();
method public final boolean dispatchGesture(@NonNull android.accessibilityservice.GestureDescription, @Nullable android.accessibilityservice.AccessibilityService.GestureResultCallback, @Nullable android.os.Handler);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
@@ -3029,6 +3031,8 @@
method @NonNull public final android.accessibilityservice.TouchInteractionController getTouchInteractionController(int);
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
method @NonNull public final android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays();
+ method public boolean isCacheEnabled();
+ method public boolean isNodeInCache(@NonNull android.view.accessibility.AccessibilityNodeInfo);
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
method @Deprecated protected boolean onGesture(int);
@@ -3039,6 +3043,7 @@
method public void onSystemActionsChanged();
method public final boolean performGlobalAction(int);
method public void setAccessibilityFocusAppearance(int, @ColorInt int);
+ method public boolean setCacheEnabled(boolean);
method public void setGestureDetectionPassthroughRegion(int, @NonNull android.graphics.Region);
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
method public void setTouchExplorationPassthroughRegion(int, @NonNull android.graphics.Region);
@@ -4465,6 +4470,7 @@
method public android.app.ActivityOptions setLaunchDisplayId(int);
method public android.app.ActivityOptions setLockTaskEnabled(boolean);
method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
+ method @NonNull public android.app.ActivityOptions setSplashScreenStyle(int);
method public android.os.Bundle toBundle();
method public void update(android.app.ActivityOptions);
field public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
@@ -16526,6 +16532,7 @@
method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter);
method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float);
method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect);
+ method @NonNull public static android.graphics.RenderEffect createRuntimeShaderEffect(@NonNull android.graphics.RuntimeShader, @NonNull String);
method @NonNull public static android.graphics.RenderEffect createShaderEffect(@NonNull android.graphics.Shader);
}
@@ -20246,6 +20253,7 @@
public final class GnssMeasurementRequest implements android.os.Parcelable {
method public int describeContents();
+ method @IntRange(from=0) public int getIntervalMillis();
method public boolean isFullTracking();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssMeasurementRequest> CREATOR;
@@ -20256,6 +20264,7 @@
ctor public GnssMeasurementRequest.Builder(@NonNull android.location.GnssMeasurementRequest);
method @NonNull public android.location.GnssMeasurementRequest build();
method @NonNull public android.location.GnssMeasurementRequest.Builder setFullTracking(boolean);
+ method @NonNull public android.location.GnssMeasurementRequest.Builder setIntervalMillis(@IntRange(from=0) int);
}
public final class GnssMeasurementsEvent implements android.os.Parcelable {
@@ -32477,6 +32486,7 @@
public final class SystemClock {
method @NonNull public static java.time.Clock currentGnssTimeClock();
+ method @NonNull public static java.time.Clock currentNetworkTimeClock();
method public static long currentThreadTimeMillis();
method public static long elapsedRealtime();
method public static long elapsedRealtimeNanos();
@@ -34283,6 +34293,7 @@
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
+ field public static final int PRESENTATION_UNAVAILABLE = 5; // 0x5
field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
field public static final String PRIORITY = "priority";
field public static final int PRIORITY_NORMAL = 0; // 0x0
@@ -35712,6 +35723,7 @@
field public static final String ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS";
field public static final String ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS";
field public static final String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
+ field public static final String ACTION_APP_LOCALE_SETTINGS = "android.settings.APP_LOCALE_SETTINGS";
field public static final String ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS = "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS";
field public static final String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS";
field public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS = "android.settings.APP_OPEN_BY_DEFAULT_SETTINGS";
@@ -41055,6 +41067,7 @@
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
+ field public static final int PRESENTATION_UNAVAILABLE = 5; // 0x5
field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
field public static final int PRIORITY_NORMAL = 0; // 0x0
field public static final int PRIORITY_URGENT = 1; // 0x1
@@ -41473,6 +41486,7 @@
field public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL = "editable_wfc_roaming_mode_bool";
field public static final String KEY_EMERGENCY_NOTIFICATION_DELAY_INT = "emergency_notification_delay_int";
field public static final String KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY = "emergency_number_prefix_string_array";
+ field public static final String KEY_ENABLE_CROSS_SIM_CALLING_ON_OPPORTUNISTIC_DATA_BOOL = "enable_cross_sim_calling_on_opportunistic_data_bool";
field public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
field public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = "enhanced_4g_lte_title_variant_int";
@@ -43939,7 +43953,7 @@
method public boolean isSimPortAvailable(int);
method public void startResolutionActivity(android.app.Activity, int, android.content.Intent, android.app.PendingIntent) throws android.content.IntentSender.SendIntentException;
method @Deprecated @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, android.app.PendingIntent);
- method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccManager.ResultListener);
+ method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, int, @NonNull android.app.PendingIntent);
method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void updateSubscriptionNickname(int, @Nullable String, @NonNull android.app.PendingIntent);
field public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
field public static final String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE = "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
@@ -43985,10 +43999,6 @@
field public static final int OPERATION_SYSTEM = 1; // 0x1
}
- public static interface EuiccManager.ResultListener {
- method public void onComplete(int, @Nullable android.content.Intent);
- }
-
}
package android.telephony.gsm {
@@ -51385,9 +51395,9 @@
method public int getRecordCount();
method public int getWindowChanges();
method public void initFromParcel(android.os.Parcel);
- method public static android.view.accessibility.AccessibilityEvent obtain(int);
- method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
- method public static android.view.accessibility.AccessibilityEvent obtain();
+ method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(int);
+ method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
+ method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain();
method public void setAction(int);
method public void setContentChangeTypes(int);
method public void setEventTime(long);
@@ -51575,13 +51585,13 @@
method public boolean isShowingHintText();
method public boolean isTextEntryKey();
method public boolean isVisibleToUser();
- method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
- method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
- method public static android.view.accessibility.AccessibilityNodeInfo obtain();
- method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.accessibility.AccessibilityNodeInfo);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain();
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.accessibility.AccessibilityNodeInfo);
method public boolean performAction(int);
method public boolean performAction(int, android.os.Bundle);
- method public void recycle();
+ method @Deprecated public void recycle();
method public boolean refresh();
method public boolean refreshWithExtraData(String, android.os.Bundle);
method @Deprecated public void removeAction(int);
@@ -51760,8 +51770,8 @@
method public int getRowCount();
method public int getSelectionMode();
method public boolean isHierarchical();
- method public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean);
- method public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean, int);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean, int);
field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
field public static final int SELECTION_MODE_NONE = 0; // 0x0
field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
@@ -51778,9 +51788,9 @@
method @Nullable public String getRowTitle();
method @Deprecated public boolean isHeading();
method public boolean isSelected();
- method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean);
- method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
- method @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
+ method @Deprecated @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
}
public static final class AccessibilityNodeInfo.CollectionItemInfo.Builder {
@@ -51808,7 +51818,7 @@
method public float getMax();
method public float getMin();
method public int getType();
- method public static android.view.accessibility.AccessibilityNodeInfo.RangeInfo obtain(int, float, float, float);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.RangeInfo obtain(int, float, float, float);
field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
field public static final int RANGE_TYPE_INT = 0; // 0x0
field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
@@ -51862,9 +51872,9 @@
method public boolean isFullScreen();
method public boolean isPassword();
method public boolean isScrollable();
- method public static android.view.accessibility.AccessibilityRecord obtain(android.view.accessibility.AccessibilityRecord);
- method public static android.view.accessibility.AccessibilityRecord obtain();
- method public void recycle();
+ method @Deprecated public static android.view.accessibility.AccessibilityRecord obtain(android.view.accessibility.AccessibilityRecord);
+ method @Deprecated public static android.view.accessibility.AccessibilityRecord obtain();
+ method @Deprecated public void recycle();
method public void setAddedCount(int);
method public void setBeforeText(CharSequence);
method public void setChecked(boolean);
@@ -57668,6 +57678,8 @@
method public void clearOnExitAnimationListener();
method public void setOnExitAnimationListener(@NonNull android.window.SplashScreen.OnExitAnimationListener);
method public void setSplashScreenTheme(@StyleRes int);
+ field public static final int SPLASH_SCREEN_STYLE_EMPTY = 0; // 0x0
+ field public static final int SPLASH_SCREEN_STYLE_ICON = 1; // 0x1
}
public static interface SplashScreen.OnExitAnimationListener {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 04c1e9e..53bc8a6 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -258,6 +258,43 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStateSnapshot> CREATOR;
}
+ public final class NetworkTemplate implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getDefaultNetworkStatus();
+ method public int getMatchRule();
+ method public int getMeteredness();
+ method public int getOemManaged();
+ method public int getRatType();
+ method public int getRoaming();
+ method @NonNull public java.util.Set<java.lang.String> getSubscriberIds();
+ method @NonNull public java.util.Set<java.lang.String> getWifiNetworkKeys();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkTemplate> CREATOR;
+ field public static final int MATCH_BLUETOOTH = 8; // 0x8
+ field public static final int MATCH_CARRIER = 10; // 0xa
+ field public static final int MATCH_ETHERNET = 5; // 0x5
+ field public static final int MATCH_MOBILE = 1; // 0x1
+ field public static final int MATCH_WIFI = 4; // 0x4
+ field public static final int NETWORK_TYPE_ALL = -1; // 0xffffffff
+ field public static final int OEM_MANAGED_ALL = -1; // 0xffffffff
+ field public static final int OEM_MANAGED_NO = 0; // 0x0
+ field public static final int OEM_MANAGED_PAID = 1; // 0x1
+ field public static final int OEM_MANAGED_PRIVATE = 2; // 0x2
+ field public static final int OEM_MANAGED_YES = -2; // 0xfffffffe
+ }
+
+ public static final class NetworkTemplate.Builder {
+ ctor public NetworkTemplate.Builder(int);
+ method @NonNull public android.net.NetworkTemplate build();
+ method @NonNull public android.net.NetworkTemplate.Builder setDefaultNetworkStatus(int);
+ method @NonNull public android.net.NetworkTemplate.Builder setMeteredness(int);
+ method @NonNull public android.net.NetworkTemplate.Builder setOemManaged(int);
+ method @NonNull public android.net.NetworkTemplate.Builder setRatType(int);
+ method @NonNull public android.net.NetworkTemplate.Builder setRoaming(int);
+ method @NonNull public android.net.NetworkTemplate.Builder setSubscriberIds(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull public android.net.NetworkTemplate.Builder setWifiNetworkKeys(@NonNull java.util.Set<java.lang.String>);
+ }
+
public class NetworkWatchlistManager {
method @Nullable public byte[] getWatchlistConfigHash();
}
@@ -342,6 +379,10 @@
method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException;
}
+ public final class StrictMode {
+ method public static void noteUntaggedSocket();
+ }
+
public class SystemConfigManager {
method @NonNull public java.util.List<android.content.ComponentName> getEnabledComponentOverrides(@NonNull String);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 75c9be9..1f96a24 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -298,6 +298,7 @@
field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
field public static final String SOUND_TRIGGER_RUN_IN_BATTERY_SAVER = "android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER";
field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
+ field public static final String START_REVIEW_PERMISSION_DECISIONS = "android.permission.START_REVIEW_PERMISSION_DECISIONS";
field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
@@ -341,6 +342,7 @@
public static final class R.array {
field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
+ field public static final int config_optionalIpSecAlgorithms;
}
public static final class R.attr {
@@ -857,7 +859,11 @@
public class StatusBarManager {
method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarModeOverride();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setNavBarModeOverride(int);
+ field public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1; // 0x1
+ field public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0; // 0x0
}
public static final class StatusBarManager.DisableInfo {
@@ -2591,6 +2597,7 @@
field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
field public static final String MUSIC_RECOGNITION_SERVICE = "music_recognition";
+ field public static final String NEARBY_SERVICE = "nearby";
field public static final String NETD_SERVICE = "netd";
field @Deprecated public static final String NETWORK_SCORE_SERVICE = "network_score";
field public static final String OEM_LOCK_SERVICE = "oem_lock";
@@ -2661,6 +2668,7 @@
field public static final String ACTION_PENDING_INCIDENT_REPORTS_CHANGED = "android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED";
field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
+ field public static final String ACTION_REFRESH_SAFETY_SOURCES = "android.intent.action.REFRESH_SAFETY_SOURCES";
field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
field @RequiresPermission(android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES) public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_ONGOING_PERMISSION_USAGE = "android.intent.action.REVIEW_ONGOING_PERMISSION_USAGE";
@@ -2691,6 +2699,10 @@
field public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
field public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
field public static final String EXTRA_REASON = "android.intent.extra.REASON";
+ field public static final int EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA = 0; // 0x0
+ field public static final int EXTRA_REFRESH_REQUEST_TYPE_GET_DATA = 1; // 0x1
+ field public static final String EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE = "android.intent.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE";
+ field public static final String EXTRA_REFRESH_SAFETY_SOURCE_IDS = "android.intent.extra.REFRESH_SAFETY_SOURCE_IDS";
field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
@@ -2910,6 +2922,7 @@
}
public static class LauncherApps.ShortcutQuery {
+ field public static final int FLAG_GET_PERSISTED_DATA = 4096; // 0x1000
field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
}
@@ -5138,6 +5151,9 @@
public static final class GnssSingleSatCorrection.Builder {
ctor public GnssSingleSatCorrection.Builder();
method @NonNull public android.location.GnssSingleSatCorrection build();
+ method @NonNull public android.location.GnssSingleSatCorrection.Builder clearExcessPathLengthMeters();
+ method @NonNull public android.location.GnssSingleSatCorrection.Builder clearExcessPathLengthUncertaintyMeters();
+ method @NonNull public android.location.GnssSingleSatCorrection.Builder clearProbabilityLineOfSight();
method @NonNull public android.location.GnssSingleSatCorrection.Builder setCarrierFrequencyHz(@FloatRange(from=0.0f, fromInclusive=false) float);
method @NonNull public android.location.GnssSingleSatCorrection.Builder setConstellationType(int);
method @NonNull public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(@FloatRange(from=0.0f) float);
@@ -5663,6 +5679,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioTrack getCallUplinkInjectionAudioTrack(@NonNull android.media.AudioFormat);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
+ method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int getLastAudibleStreamVolume(int);
method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
@@ -6469,10 +6486,11 @@
method public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
method public long getAvSyncTime(int);
method @Nullable public java.util.List<android.media.tv.tuner.frontend.FrontendInfo> getAvailableFrontendInfos();
- method @Nullable public String getCurrentFrontendHardwardInfo();
+ method @Nullable public String getCurrentFrontendHardwareInfo();
method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
+ method @IntRange(from=0xffffffff) public int getMaxNumberOfFrontends(int);
method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean hasUnusedFrontend(int);
method public boolean isLowestPriority(int);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
@@ -6485,6 +6503,7 @@
method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter();
method public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
method public int setLnaEnabled(boolean);
+ method public int setMaxNumberOfFrontends(int, @IntRange(from=0) int);
method public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
@@ -7917,7 +7936,7 @@
method public void onScanStopped();
method public void onSignalTypeReported(int);
method public void onSymbolRatesReported(@NonNull int[]);
- method public default void onUnLocked();
+ method public default void onUnlocked();
}
}
@@ -8077,13 +8096,17 @@
method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR;
+ field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff
field public static final int DEFAULT_NETWORK_NO = 0; // 0x0
field public static final int DEFAULT_NETWORK_YES = 1; // 0x1
field public static final String IFACE_VT = "vt_data0";
+ field public static final int METERED_ALL = -1; // 0xffffffff
field public static final int METERED_NO = 0; // 0x0
field public static final int METERED_YES = 1; // 0x1
+ field public static final int ROAMING_ALL = -1; // 0xffffffff
field public static final int ROAMING_NO = 0; // 0x0
field public static final int ROAMING_YES = 1; // 0x1
+ field public static final int SET_ALL = -1; // 0xffffffff
field public static final int SET_DEFAULT = 0; // 0x0
field public static final int SET_FOREGROUND = 1; // 0x1
field public static final int TAG_NONE = 0; // 0x0
@@ -9527,6 +9550,7 @@
public final class PermissionControllerManager {
method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
+ method public void getUnusedAppCount(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
field public static final int COUNT_ONLY_WHEN_GRANTED = 1; // 0x1
@@ -9551,6 +9575,7 @@
method @BinderThread public abstract void onGetPermissionUsages(boolean, long, @NonNull java.util.function.Consumer<java.util.List<android.permission.RuntimePermissionUsageInfo>>);
method @BinderThread public void onGetPlatformPermissionsForGroup(@NonNull String, @NonNull java.util.function.Consumer<java.util.List<java.lang.String>>);
method @BinderThread public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream, @NonNull Runnable);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetUnusedAppCount(@NonNull java.util.function.IntConsumer);
method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable);
method @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
@@ -9577,6 +9602,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String);
+ field @RequiresPermission(android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS) public static final String ACTION_REVIEW_PERMISSION_DECISIONS = "android.permission.action.REVIEW_PERMISSION_DECISIONS";
field public static final int PERMISSION_GRANTED = 0; // 0x0
field public static final int PERMISSION_HARD_DENIED = 2; // 0x2
field public static final int PERMISSION_SOFT_DENIED = 1; // 0x1
@@ -9998,7 +10024,7 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
field public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios";
field public static final String APP_STANDBY_ENABLED = "app_standby_enabled";
- field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
+ field @Deprecated public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
field public static final String CARRIER_APP_NAMES = "carrier_app_names";
field public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
field public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
@@ -10712,6 +10738,7 @@
field public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
field public static final String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT";
field public static final String EXTRA_RESOLUTION_PORT_INDEX = "android.service.euicc.extra.RESOLUTION_PORT_INDEX";
+ field public static final String EXTRA_RESOLUTION_USE_PORT_INDEX = "android.service.euicc.extra.RESOLUTION_USE_PORT_INDEX";
field public static final String EXTRA_RESOLVABLE_ERRORS = "android.service.euicc.extra.RESOLVABLE_ERRORS";
field public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1; // 0x1
field public static final int RESOLVABLE_ERROR_POLICY_RULES = 2; // 0x2
@@ -13758,6 +13785,7 @@
field public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2; // 0x2
field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1
+ field public static final int OIR_PRESENTATION_UNAVAILABLE = 5; // 0x5
field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3
field public static final int PRIORITY_NORMAL = 0; // 0x0
field public static final int PRIORITY_URGENT = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b37c938..8724b53 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -59,6 +59,7 @@
public static final class R.bool {
field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
+ field public static final int config_preventImeStartupUnlessTextEditor;
field public static final int config_remoteInsetsControllerControlsSystemBars = 17891334; // 0x1110006
}
@@ -2837,7 +2838,6 @@
method public void addChild(@NonNull android.os.IBinder);
method public long getSourceNodeId();
method public void setLeashedParent(@Nullable android.os.IBinder, int);
- method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
method public void writeToParcelNoRecycle(android.os.Parcel, int);
}
@@ -2877,6 +2877,7 @@
}
public final class AutofillManager {
+ field public static final String DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "compat_mode_allowed_packages";
field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes";
field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0
field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 343830a..c9cbef2 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -301,6 +301,17 @@
],
}
+cc_library {
+ name: "libactivity_manager_procstate_aidl-cpp",
+ host_supported: true,
+ srcs: [
+ ":activity_manager_procstate_aidl",
+ ],
+ aidl: {
+ export_aidl_headers: true,
+ },
+}
+
// Build Rust bindings for PermissionController. Needed by keystore2.
aidl_interface {
name: "android.os.permissions_aidl",
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3d38551..9a55867 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -59,6 +59,7 @@
import android.view.SurfaceView;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
+import android.view.accessibility.AccessibilityCache;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -748,7 +749,6 @@
private FingerprintGestureController mFingerprintGestureController;
-
/**
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
*
@@ -2076,6 +2076,85 @@
available);
}
+ /** Sets the cache status.
+ *
+ * <p>If {@code enabled}, enable the cache and prefetching. Otherwise, disable the cache
+ * and prefetching.
+ * Note: By default the cache is enabled.
+ * @param enabled whether to enable or disable the cache.
+ * @return {@code true} if the cache and connection are not null, so the cache status is set.
+ */
+ public boolean setCacheEnabled(boolean enabled) {
+ AccessibilityCache cache =
+ AccessibilityInteractionClient.getCache(mConnectionId);
+ if (cache == null) {
+ return false;
+ }
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getConnection(mConnectionId);
+ if (connection == null) {
+ return false;
+ }
+ try {
+ connection.setCacheEnabled(enabled);
+ cache.setEnabled(enabled);
+ return true;
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting status of cache", re);
+ re.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ /** Invalidates {@code node} and its subtree in the cache.
+ * @param node the node to invalidate.
+ * @return {@code true} if the subtree rooted at {@code node} was invalidated.
+ */
+ public boolean clearCachedSubtree(@NonNull AccessibilityNodeInfo node) {
+ AccessibilityCache cache =
+ AccessibilityInteractionClient.getCache(mConnectionId);
+ if (cache == null) {
+ return false;
+ }
+ return cache.clearSubTree(node);
+ }
+
+ /** Clears the cache.
+ * @return {@code true} if the cache was cleared
+ */
+ public boolean clearCache() {
+ AccessibilityCache cache =
+ AccessibilityInteractionClient.getCache(mConnectionId);
+ if (cache == null) {
+ return false;
+ }
+ cache.clear();
+ return true;
+ }
+
+ /** Checks if {@code node} is in the cache.
+ * @param node the node to check.
+ * @return {@code true} if {@code node} is in the cache.
+ */
+ public boolean isNodeInCache(@NonNull AccessibilityNodeInfo node) {
+ AccessibilityCache cache =
+ AccessibilityInteractionClient.getCache(mConnectionId);
+ if (cache == null) {
+ return false;
+ }
+ return cache.isNodeInCache(node);
+ }
+
+ /** Returns {@code true} if the cache is enabled. */
+ public boolean isCacheEnabled() {
+ AccessibilityCache cache =
+ AccessibilityInteractionClient.getCache(mConnectionId);
+ if (cache == null) {
+ return false;
+ }
+ return cache.isEnabled();
+ }
+
/** This is called when the system action list is changed. */
public void onSystemActionsChanged() {
}
@@ -2613,11 +2692,11 @@
mCallback.init(mConnectionId, windowToken);
mCallback.onServiceConnected();
} else {
+ AccessibilityInteractionClient.getInstance(mContext)
+ .clearCache(mConnectionId);
AccessibilityInteractionClient.getInstance(mContext).removeConnection(
mConnectionId);
mConnectionId = AccessibilityInteractionClient.NO_ID;
- AccessibilityInteractionClient.getInstance(mContext)
- .clearCache(mConnectionId);
mCallback.init(AccessibilityInteractionClient.NO_ID, null);
}
return;
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 93e6914..7d76bbf 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -124,6 +124,8 @@
void setFocusAppearance(int strokeWidth, int color);
+ void setCacheEnabled(boolean enabled);
+
oneway void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
int processId, long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index f7d5e52..5e5649f 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -337,7 +337,7 @@
private static final String KEY_LAUNCHED_FROM_BUBBLE =
"android.activity.launchTypeBubble";
- /** See {@link #setSplashscreenStyle(int)}. */
+ /** See {@link #setSplashScreenStyle(int)}. */
private static final String KEY_SPLASH_SCREEN_STYLE =
"android.activity.splashScreenStyle";
@@ -1393,20 +1393,27 @@
}
/**
- * Sets the preferred splash screen style.
+ * Gets the style can be used for cold-launching an activity.
+ * @see #setSplashScreenStyle(int)
* @hide
*/
- public void setSplashscreenStyle(@SplashScreen.SplashScreenStyle int style) {
- mSplashScreenStyle = style;
+ public @SplashScreen.SplashScreenStyle int getSplashScreenStyle() {
+ return mSplashScreenStyle;
}
/**
- * Gets the preferred splash screen style from caller
- * @hide
+ * Sets the preferred splash screen style of the opening activities. This only applies if the
+ * Activity or Process is not yet created.
+ * @param style Can be either {@link SplashScreen#SPLASH_SCREEN_STYLE_ICON} or
+ * {@link SplashScreen#SPLASH_SCREEN_STYLE_EMPTY}
*/
- @SplashScreen.SplashScreenStyle
- public int getSplashScreenStyle() {
- return mSplashScreenStyle;
+ @NonNull
+ public ActivityOptions setSplashScreenStyle(@SplashScreen.SplashScreenStyle int style) {
+ if (style == SplashScreen.SPLASH_SCREEN_STYLE_ICON
+ || style == SplashScreen.SPLASH_SCREEN_STYLE_EMPTY) {
+ mSplashScreenStyle = style;
+ }
+ return this;
}
/**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f3e9f10..c895636 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1796,6 +1796,7 @@
&& ((flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
flags = flags | Context.RECEIVER_EXPORTED;
}
+
final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
@@ -1810,6 +1811,9 @@
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (WtfException e) {
+ Log.wtf(TAG, e.getMessage());
+ return null;
}
}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index ae578f5..64d3a9f 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -282,6 +282,33 @@
@Retention(RetentionPolicy.SOURCE)
public @interface RequestResult {}
+ /**
+ * Constant for {@link #setNavBarModeOverride(int)} indicating the default navbar mode.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0;
+
+ /**
+ * Constant for {@link #setNavBarModeOverride(int)} indicating kids navbar mode.
+ *
+ * <p>When used, back and home icons will change drawables and layout, recents will be hidden,
+ * and the navbar will remain visible when apps are in immersive mode.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1;
+
+ /** @hide */
+ @IntDef(prefix = {"NAV_BAR_MODE_OVERRIDE_"}, value = {
+ NAV_BAR_MODE_OVERRIDE_NONE,
+ NAV_BAR_MODE_OVERRIDE_KIDS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NavBarModeOverride {}
+
@UnsupportedAppUsage
private Context mContext;
private IStatusBarService mService;
@@ -687,6 +714,52 @@
}
}
+ /**
+ * Sets or removes the navigation bar mode override.
+ *
+ * @param navBarModeOverride the mode of the navigation bar override to be set.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.STATUS_BAR)
+ public void setNavBarModeOverride(@NavBarModeOverride int navBarModeOverride) {
+ if (navBarModeOverride != NAV_BAR_MODE_OVERRIDE_NONE
+ && navBarModeOverride != NAV_BAR_MODE_OVERRIDE_KIDS) {
+ throw new UnsupportedOperationException(
+ "Supplied navBarModeOverride not supported: " + navBarModeOverride);
+ }
+
+ try {
+ final IStatusBarService svc = getService();
+ if (svc != null) {
+ svc.setNavBarModeOverride(navBarModeOverride);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the navigation bar mode override. Returns default value if no override is set.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.STATUS_BAR)
+ public @NavBarModeOverride int getNavBarModeOverride() {
+ int navBarModeOverride = NAV_BAR_MODE_OVERRIDE_NONE;
+ try {
+ final IStatusBarService svc = getService();
+ if (svc != null) {
+ navBarModeOverride = svc.getNavBarModeOverride();
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return navBarModeOverride;
+ }
+
/** @hide */
public static String windowStateToString(int state) {
if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
diff --git a/core/java/android/app/WtfException.java b/core/java/android/app/WtfException.java
new file mode 100644
index 0000000..ba8dbef
--- /dev/null
+++ b/core/java/android/app/WtfException.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Exception meant to be thrown instead of calling Log.wtf() such that server side code can
+ * throw this exception, and it will carry across the binder to do client side logging.
+ * {@hide}
+ */
+public final class WtfException extends RuntimeException implements Parcelable {
+ public static final @android.annotation.NonNull
+ Creator<WtfException> CREATOR = new Creator<WtfException>() {
+ @Override
+ public WtfException createFromParcel(Parcel source) {
+ return new WtfException(source.readString8());
+ }
+
+ @Override
+ public WtfException[] newArray(int size) {
+ return new WtfException[size];
+ }
+ };
+
+ public WtfException(@android.annotation.NonNull String message) {
+ super(message);
+ }
+
+ /** {@hide} */
+ public static Throwable readFromParcel(Parcel in) {
+ final String msg = in.readString8();
+ return new WtfException(msg);
+ }
+
+ /** {@hide} */
+ public static void writeToParcel(Parcel out, Throwable t) {
+ out.writeString8(t.getMessage());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
+ dest.writeString8(getMessage());
+ }
+}
+
diff --git a/core/java/android/app/communal/CommunalManager.java b/core/java/android/app/communal/CommunalManager.java
index 22f07693..22bd9d1 100644
--- a/core/java/android/app/communal/CommunalManager.java
+++ b/core/java/android/app/communal/CommunalManager.java
@@ -23,9 +23,6 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
-import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
@@ -46,31 +43,10 @@
private final ICommunalManager mService;
private final ArrayMap<CommunalModeListener, ICommunalModeListener> mCommunalModeListeners;
- /**
- * This change id is used to annotate packages which can run in communal mode by default,
- * without requiring user opt-in.
- *
- * @hide
- */
- @ChangeId
- @Overridable
- @Disabled
- public static final long ALLOW_COMMUNAL_MODE_BY_DEFAULT = 203673428L;
-
- /**
- * This change id is used to annotate packages which are allowed to run in communal mode.
- *
- * @hide
- */
- @ChangeId
- @Overridable
- @Disabled
- public static final long ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT = 200324021L;
-
/** @hide */
public CommunalManager(ICommunalManager service) {
mService = service;
- mCommunalModeListeners = new ArrayMap<CommunalModeListener, ICommunalModeListener>();
+ mCommunalModeListeners = new ArrayMap<>();
}
/**
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2d1ecfb..9a0f02e 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3064,6 +3064,9 @@
BluetoothCsipSetCoordinator csipSetCoordinator =
new BluetoothCsipSetCoordinator(context, listener, this);
return true;
+ } else if (profile == BluetoothProfile.LE_CALL_CONTROL) {
+ BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener);
+ return true;
} else {
return false;
}
@@ -3166,6 +3169,10 @@
(BluetoothCsipSetCoordinator) proxy;
csipSetCoordinator.close();
break;
+ case BluetoothProfile.LE_CALL_CONTROL:
+ BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy;
+ tbs.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index fe8d1ba..b531829 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -87,7 +87,7 @@
private static final int CONN_STATE_CLOSED = 4;
private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5;
- private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 1000; // milliseconds
+ private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 10; // milliseconds
private List<BluetoothGattService> mServices;
diff --git a/core/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java b/core/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java
new file mode 100644
index 0000000..cb47280
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java
@@ -0,0 +1,788 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * This class represents an LE Audio Broadcast Source and the associated information that is needed
+ * by Broadcast Audio Scan Service (BASS) residing on a Scan Delegator.
+ *
+ * <p>For example, the Scan Delegator on an LE Audio Broadcast Sink can use the information
+ * contained within an instance of this class to synchronize with an LE Audio Broadcast Source in
+ * order to listen to a Broadcast Audio Stream.
+ *
+ * <p>BroadcastAssistant has a BASS client which facilitates scanning and discovery of Broadcast
+ * Sources on behalf of say a Broadcast Sink. Upon successful discovery of one or more Broadcast
+ * sources, this information needs to be communicated to the BASS Server residing within the Scan
+ * Delegator on a Broadcast Sink. This is achieved using the Periodic Advertising Synchronization
+ * Transfer (PAST) procedure. This procedure uses information contained within an instance of this
+ * class.
+ *
+ * @hide
+ */
+public final class BluetoothLeBroadcastSourceInfo implements Parcelable {
+ private static final String TAG = "BluetoothLeBroadcastSourceInfo";
+ private static final boolean DBG = true;
+
+ /**
+ * Constants representing Broadcast Source address types
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = "LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_",
+ value = {
+ LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_PUBLIC,
+ LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_RANDOM,
+ LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LeAudioBroadcastSourceAddressType {}
+
+ /**
+ * Represents a public address used by an LE Audio Broadcast Source
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_PUBLIC = 0;
+
+ /**
+ * Represents a random address used by an LE Audio Broadcast Source
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_RANDOM = 1;
+
+ /**
+ * Represents an invalid address used by an LE Audio Broadcast Seurce
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID = 0xFFFF;
+
+ /**
+ * Periodic Advertising Synchronization state
+ *
+ * <p>Periodic Advertising (PA) enables the LE Audio Broadcast Assistant to discover broadcast
+ * audio streams as well as the audio stream configuration on behalf of an LE Audio Broadcast
+ * Sink. This information can then be transferred to the LE Audio Broadcast Sink using the
+ * Periodic Advertising Synchronizaton Transfer (PAST) procedure.
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = "LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_",
+ value = {
+ LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IDLE,
+ LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNCINFO_REQ,
+ LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IN_SYNC,
+ LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNC_FAIL,
+ LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_NO_PAST
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LeAudioBroadcastSinkPaSyncState {}
+
+ /**
+ * Indicates that the Broadcast Sink is not synchronized with the Periodic Advertisements (PA)
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IDLE = 0;
+
+ /**
+ * Indicates that the Broadcast Sink requested the Broadcast Assistant to synchronize with the
+ * Periodic Advertisements (PA).
+ *
+ * <p>This is also known as scan delegation or scan offloading.
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNCINFO_REQ = 1;
+
+ /**
+ * Indicates that the Broadcast Sink is synchronized with the Periodic Advertisements (PA).
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IN_SYNC = 2;
+
+ /**
+ * Indicates that the Broadcast Sink was unable to synchronize with the Periodic Advertisements
+ * (PA).
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNC_FAIL = 3;
+
+ /**
+ * Indicates that the Broadcast Sink should be synchronized with the Periodic Advertisements
+ * (PA) using the Periodic Advertisements Synchronization Transfert (PAST) procedure.
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_NO_PAST = 4;
+
+ /**
+ * Indicates that the Broadcast Sink synchornization state is invalid.
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID = 0xFFFF;
+
+ /** @hide */
+ @IntDef(
+ prefix = "LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_",
+ value = {
+ LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED,
+ LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_SYNCHRONIZED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LeAudioBroadcastSinkAudioSyncState {}
+
+ /**
+ * Indicates that the Broadcast Sink is not synchronized with a Broadcast Audio Stream.
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED = 0;
+
+ /**
+ * Indicates that the Broadcast Sink is synchronized with a Broadcast Audio Stream.
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_SYNCHRONIZED = 1;
+
+ /**
+ * Indicates that the Broadcast Sink audio synchronization state is invalid.
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID = 0xFFFF;
+
+ /** @hide */
+ @IntDef(
+ prefix = "LE_AUDIO_BROADCAST_SINK_ENC_STATE_",
+ value = {
+ LE_AUDIO_BROADCAST_SINK_ENC_STATE_NOT_ENCRYPTED,
+ LE_AUDIO_BROADCAST_SINK_ENC_STATE_CODE_REQUIRED,
+ LE_AUDIO_BROADCAST_SINK_ENC_STATE_DECRYPTING,
+ LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LeAudioBroadcastSinkEncryptionState {}
+
+ /**
+ * Indicates that the Broadcast Sink is synchronized with an unencrypted audio stream.
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_NOT_ENCRYPTED = 0;
+
+ /**
+ * Indicates that the Broadcast Sink needs a Broadcast Code to synchronize with the audio
+ * stream.
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_CODE_REQUIRED = 1;
+
+ /**
+ * Indicates that the Broadcast Sink is synchronized with an encrypted audio stream.
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_DECRYPTING = 2;
+
+ /**
+ * Indicates that the Broadcast Sink is unable to decrypt an audio stream due to an incorrect
+ * Broadcast Code
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE = 3;
+
+ /**
+ * Indicates that the Broadcast Sink encryption state is invalid.
+ *
+ * @hide
+ */
+ public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID = 0xFF;
+
+ /**
+ * Represents an invalid LE Audio Broadcast Source ID
+ *
+ * @hide
+ */
+ public static final byte LE_AUDIO_BROADCAST_SINK_INVALID_SOURCE_ID = (byte) 0x00;
+
+ /**
+ * Represents an invalid Broadcast ID of a Broadcast Source
+ *
+ * @hide
+ */
+ public static final int INVALID_BROADCAST_ID = 0xFFFFFF;
+
+ private byte mSourceId;
+ private @LeAudioBroadcastSourceAddressType int mSourceAddressType;
+ private BluetoothDevice mSourceDevice;
+ private byte mSourceAdvSid;
+ private int mBroadcastId;
+ private @LeAudioBroadcastSinkPaSyncState int mPaSyncState;
+ private @LeAudioBroadcastSinkEncryptionState int mEncryptionStatus;
+ private @LeAudioBroadcastSinkAudioSyncState int mAudioSyncState;
+ private byte[] mBadBroadcastCode;
+ private byte mNumSubGroups;
+ private Map<Integer, Integer> mSubgroupBisSyncState = new HashMap<Integer, Integer>();
+ private Map<Integer, byte[]> mSubgroupMetadata = new HashMap<Integer, byte[]>();
+
+ private String mBroadcastCode;
+ private static final int BIS_NO_PREF = 0xFFFFFFFF;
+ private static final int BROADCAST_CODE_SIZE = 16;
+
+ /**
+ * Constructor to create an Empty object of {@link BluetoothLeBroadcastSourceInfo } with the
+ * given Source Id.
+ *
+ * <p>This is mainly used to represent the Empty Broadcast Source entries
+ *
+ * @param sourceId Source Id for this Broadcast Source info object
+ * @hide
+ */
+ public BluetoothLeBroadcastSourceInfo(byte sourceId) {
+ mSourceId = sourceId;
+ mSourceAddressType = LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID;
+ mSourceDevice = null;
+ mSourceAdvSid = (byte) 0x00;
+ mBroadcastId = INVALID_BROADCAST_ID;
+ mPaSyncState = LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID;
+ mAudioSyncState = LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID;
+ mEncryptionStatus = LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID;
+ mBadBroadcastCode = null;
+ mNumSubGroups = 0;
+ mBroadcastCode = null;
+ }
+
+ /*package*/ BluetoothLeBroadcastSourceInfo(
+ byte sourceId,
+ @LeAudioBroadcastSourceAddressType int addressType,
+ @NonNull BluetoothDevice device,
+ byte advSid,
+ int broadcastId,
+ @LeAudioBroadcastSinkPaSyncState int paSyncstate,
+ @LeAudioBroadcastSinkEncryptionState int encryptionStatus,
+ @LeAudioBroadcastSinkAudioSyncState int audioSyncstate,
+ @Nullable byte[] badCode,
+ byte numSubGroups,
+ @NonNull Map<Integer, Integer> bisSyncState,
+ @Nullable Map<Integer, byte[]> subgroupMetadata,
+ @NonNull String broadcastCode) {
+ mSourceId = sourceId;
+ mSourceAddressType = addressType;
+ mSourceDevice = device;
+ mSourceAdvSid = advSid;
+ mBroadcastId = broadcastId;
+ mPaSyncState = paSyncstate;
+ mEncryptionStatus = encryptionStatus;
+ mAudioSyncState = audioSyncstate;
+
+ if (badCode != null && badCode.length != 0) {
+ mBadBroadcastCode = new byte[badCode.length];
+ System.arraycopy(badCode, 0, mBadBroadcastCode, 0, badCode.length);
+ }
+ mNumSubGroups = numSubGroups;
+ mSubgroupBisSyncState = new HashMap<Integer, Integer>(bisSyncState);
+ mSubgroupMetadata = new HashMap<Integer, byte[]>(subgroupMetadata);
+ mBroadcastCode = broadcastCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BluetoothLeBroadcastSourceInfo) {
+ BluetoothLeBroadcastSourceInfo other = (BluetoothLeBroadcastSourceInfo) o;
+ return (other.mSourceId == mSourceId
+ && other.mSourceAddressType == mSourceAddressType
+ && other.mSourceDevice == mSourceDevice
+ && other.mSourceAdvSid == mSourceAdvSid
+ && other.mBroadcastId == mBroadcastId
+ && other.mPaSyncState == mPaSyncState
+ && other.mEncryptionStatus == mEncryptionStatus
+ && other.mAudioSyncState == mAudioSyncState
+ && Arrays.equals(other.mBadBroadcastCode, mBadBroadcastCode)
+ && other.mNumSubGroups == mNumSubGroups
+ && mSubgroupBisSyncState.equals(other.mSubgroupBisSyncState)
+ && mSubgroupMetadata.equals(other.mSubgroupMetadata)
+ && other.mBroadcastCode == mBroadcastCode);
+ }
+ return false;
+ }
+
+ /**
+ * Checks if an instance of {@link BluetoothLeBroadcastSourceInfo} is empty.
+ *
+ * @hide
+ */
+ public boolean isEmpty() {
+ boolean ret = false;
+ if (mSourceAddressType == LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID
+ && mSourceDevice == null
+ && mSourceAdvSid == (byte) 0
+ && mPaSyncState == LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID
+ && mEncryptionStatus == LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID
+ && mAudioSyncState == LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID
+ && mBadBroadcastCode == null
+ && mNumSubGroups == 0
+ && mSubgroupBisSyncState.size() == 0
+ && mSubgroupMetadata.size() == 0
+ && mBroadcastCode == null) {
+ ret = true;
+ }
+ return ret;
+ }
+
+ /**
+ * Compares an instance of {@link BluetoothLeBroadcastSourceInfo} with the provided instance.
+ *
+ * @hide
+ */
+ public boolean matches(BluetoothLeBroadcastSourceInfo srcInfo) {
+ boolean ret = false;
+ if (srcInfo == null) {
+ ret = false;
+ } else {
+ if (mSourceDevice == null) {
+ if (mSourceAdvSid == srcInfo.getAdvertisingSid()
+ && mSourceAddressType == srcInfo.getAdvAddressType()) {
+ ret = true;
+ }
+ } else {
+ if (mSourceDevice.equals(srcInfo.getSourceDevice())
+ && mSourceAdvSid == srcInfo.getAdvertisingSid()
+ && mSourceAddressType == srcInfo.getAdvAddressType()
+ && mBroadcastId == srcInfo.getBroadcastId()) {
+ ret = true;
+ }
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mSourceId,
+ mSourceAddressType,
+ mSourceDevice,
+ mSourceAdvSid,
+ mBroadcastId,
+ mPaSyncState,
+ mEncryptionStatus,
+ mAudioSyncState,
+ mBadBroadcastCode,
+ mNumSubGroups,
+ mSubgroupBisSyncState,
+ mSubgroupMetadata,
+ mBroadcastCode);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "{BluetoothLeBroadcastSourceInfo : mSourceId"
+ + mSourceId
+ + " addressType: "
+ + mSourceAddressType
+ + " sourceDevice: "
+ + mSourceDevice
+ + " mSourceAdvSid:"
+ + mSourceAdvSid
+ + " mBroadcastId:"
+ + mBroadcastId
+ + " mPaSyncState:"
+ + mPaSyncState
+ + " mEncryptionStatus:"
+ + mEncryptionStatus
+ + " mAudioSyncState:"
+ + mAudioSyncState
+ + " mBadBroadcastCode:"
+ + mBadBroadcastCode
+ + " mNumSubGroups:"
+ + mNumSubGroups
+ + " mSubgroupBisSyncState:"
+ + mSubgroupBisSyncState
+ + " mSubgroupMetadata:"
+ + mSubgroupMetadata
+ + " mBroadcastCode:"
+ + mBroadcastCode
+ + "}";
+ }
+
+ /**
+ * Get the Source Id
+ *
+ * @return byte representing the Source Id, {@link
+ * #LE_AUDIO_BROADCAST_ASSISTANT_INVALID_SOURCE_ID} if invalid
+ * @hide
+ */
+ public byte getSourceId() {
+ return mSourceId;
+ }
+
+ /**
+ * Set the Source Id
+ *
+ * @param sourceId source Id
+ * @hide
+ */
+ public void setSourceId(byte sourceId) {
+ mSourceId = sourceId;
+ }
+
+ /**
+ * Set the Broadcast Source device
+ *
+ * @param sourceDevice the Broadcast Source BluetoothDevice
+ * @hide
+ */
+ public void setSourceDevice(@NonNull BluetoothDevice sourceDevice) {
+ mSourceDevice = sourceDevice;
+ }
+
+ /**
+ * Get the Broadcast Source BluetoothDevice
+ *
+ * @return Broadcast Source BluetoothDevice
+ * @hide
+ */
+ public @NonNull BluetoothDevice getSourceDevice() {
+ return mSourceDevice;
+ }
+
+ /**
+ * Set the address type of the Broadcast Source advertisements
+ *
+ * @hide
+ */
+ public void setAdvAddressType(@LeAudioBroadcastSourceAddressType int addressType) {
+ mSourceAddressType = addressType;
+ }
+
+ /**
+ * Get the address type used by advertisements from the Broadcast Source.
+ * BluetoothLeBroadcastSourceInfo Object
+ *
+ * @hide
+ */
+ @LeAudioBroadcastSourceAddressType
+ public int getAdvAddressType() {
+ return mSourceAddressType;
+ }
+
+ /**
+ * Set the advertising SID of the Broadcast Source advertisement.
+ *
+ * @param advSid advertising SID of the Broadcast Source
+ * @hide
+ */
+ public void setAdvertisingSid(byte advSid) {
+ mSourceAdvSid = advSid;
+ }
+
+ /**
+ * Get the advertising SID of the Broadcast Source advertisement.
+ *
+ * @return advertising SID of the Broadcast Source
+ * @hide
+ */
+ public byte getAdvertisingSid() {
+ return mSourceAdvSid;
+ }
+
+ /**
+ * Get the Broadcast ID of the Broadcast Source.
+ *
+ * @return broadcast ID
+ * @hide
+ */
+ public int getBroadcastId() {
+ return mBroadcastId;
+ }
+
+ /**
+ * Set the Periodic Advertising (PA) Sync State.
+ *
+ * @hide
+ */
+ /*package*/ void setPaSyncState(@LeAudioBroadcastSinkPaSyncState int paSyncState) {
+ mPaSyncState = paSyncState;
+ }
+
+ /**
+ * Get the Periodic Advertising (PA) Sync State
+ *
+ * @hide
+ */
+ public @LeAudioBroadcastSinkPaSyncState int getMetadataSyncState() {
+ return mPaSyncState;
+ }
+
+ /**
+ * Set the audio sync state
+ *
+ * @hide
+ */
+ /*package*/ void setAudioSyncState(@LeAudioBroadcastSinkAudioSyncState int audioSyncState) {
+ mAudioSyncState = audioSyncState;
+ }
+
+ /**
+ * Get the audio sync state
+ *
+ * @hide
+ */
+ public @LeAudioBroadcastSinkAudioSyncState int getAudioSyncState() {
+ return mAudioSyncState;
+ }
+
+ /**
+ * Set the encryption status
+ *
+ * @hide
+ */
+ /*package*/ void setEncryptionStatus(
+ @LeAudioBroadcastSinkEncryptionState int encryptionStatus) {
+ mEncryptionStatus = encryptionStatus;
+ }
+
+ /**
+ * Get the encryption status
+ *
+ * @hide
+ */
+ public @LeAudioBroadcastSinkEncryptionState int getEncryptionStatus() {
+ return mEncryptionStatus;
+ }
+
+ /**
+ * Get the incorrect broadcast code that the Scan delegator used to decrypt the Broadcast Audio
+ * Stream and failed.
+ *
+ * <p>This code is valid only if {@link #getEncryptionStatus} returns {@link
+ * #LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE}
+ *
+ * @return byte array containing bad broadcast value, null if the current encryption status is
+ * not {@link #LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE}
+ * @hide
+ */
+ public @Nullable byte[] getBadBroadcastCode() {
+ return mBadBroadcastCode;
+ }
+
+ /**
+ * Get the number of subgroups.
+ *
+ * @return number of subgroups
+ * @hide
+ */
+ public byte getNumberOfSubGroups() {
+ return mNumSubGroups;
+ }
+
+ public @NonNull Map<Integer, Integer> getSubgroupBisSyncState() {
+ return mSubgroupBisSyncState;
+ }
+
+ public void setSubgroupBisSyncState(@NonNull Map<Integer, Integer> bisSyncState) {
+ mSubgroupBisSyncState = new HashMap<Integer, Integer>(bisSyncState);
+ }
+
+ /*package*/ void setBroadcastCode(@NonNull String broadcastCode) {
+ mBroadcastCode = broadcastCode;
+ }
+
+ /**
+ * Get the broadcast code
+ *
+ * @return
+ * @hide
+ */
+ public @NonNull String getBroadcastCode() {
+ return mBroadcastCode;
+ }
+
+ /**
+ * Set the broadcast ID
+ *
+ * @param broadcastId broadcast ID of the Broadcast Source
+ * @hide
+ */
+ public void setBroadcastId(int broadcastId) {
+ mBroadcastId = broadcastId;
+ }
+
+ private void writeSubgroupBisSyncStateToParcel(
+ @NonNull Parcel dest, @NonNull Map<Integer, Integer> subgroupBisSyncState) {
+ dest.writeInt(subgroupBisSyncState.size());
+ for (Map.Entry<Integer, Integer> entry : subgroupBisSyncState.entrySet()) {
+ dest.writeInt(entry.getKey());
+ dest.writeInt(entry.getValue());
+ }
+ }
+
+ private static void readSubgroupBisSyncStateFromParcel(
+ @NonNull Parcel in, @NonNull Map<Integer, Integer> subgroupBisSyncState) {
+ int size = in.readInt();
+
+ for (int i = 0; i < size; i++) {
+ Integer key = in.readInt();
+ Integer value = in.readInt();
+ subgroupBisSyncState.put(key, value);
+ }
+ }
+
+ private void writeSubgroupMetadataToParcel(
+ @NonNull Parcel dest, @Nullable Map<Integer, byte[]> subgroupMetadata) {
+ if (subgroupMetadata == null) {
+ dest.writeInt(0);
+ return;
+ }
+
+ dest.writeInt(subgroupMetadata.size());
+ for (Map.Entry<Integer, byte[]> entry : subgroupMetadata.entrySet()) {
+ dest.writeInt(entry.getKey());
+ byte[] metadata = entry.getValue();
+ if (metadata != null) {
+ dest.writeInt(metadata.length);
+ dest.writeByteArray(metadata);
+ }
+ }
+ }
+
+ private static void readSubgroupMetadataFromParcel(
+ @NonNull Parcel in, @NonNull Map<Integer, byte[]> subgroupMetadata) {
+ int size = in.readInt();
+
+ for (int i = 0; i < size; i++) {
+ Integer key = in.readInt();
+ Integer metaDataLen = in.readInt();
+ byte[] metadata = null;
+ if (metaDataLen != 0) {
+ metadata = new byte[metaDataLen];
+ in.readByteArray(metadata);
+ }
+ subgroupMetadata.put(key, metadata);
+ }
+ }
+
+ public static final @NonNull Parcelable.Creator<BluetoothLeBroadcastSourceInfo> CREATOR =
+ new Parcelable.Creator<BluetoothLeBroadcastSourceInfo>() {
+ public @NonNull BluetoothLeBroadcastSourceInfo createFromParcel(
+ @NonNull Parcel in) {
+ final byte sourceId = in.readByte();
+ final int sourceAddressType = in.readInt();
+ final BluetoothDevice sourceDevice =
+ in.readTypedObject(BluetoothDevice.CREATOR);
+ final byte sourceAdvSid = in.readByte();
+ final int broadcastId = in.readInt();
+ final int paSyncState = in.readInt();
+ final int audioSyncState = in.readInt();
+ final int encryptionStatus = in.readInt();
+ final int badBroadcastLen = in.readInt();
+ byte[] badBroadcastCode = null;
+
+ if (badBroadcastLen > 0) {
+ badBroadcastCode = new byte[badBroadcastLen];
+ in.readByteArray(badBroadcastCode);
+ }
+ final byte numSubGroups = in.readByte();
+ final String broadcastCode = in.readString();
+ Map<Integer, Integer> subgroupBisSyncState = new HashMap<Integer, Integer>();
+ readSubgroupBisSyncStateFromParcel(in, subgroupBisSyncState);
+ Map<Integer, byte[]> subgroupMetadata = new HashMap<Integer, byte[]>();
+ readSubgroupMetadataFromParcel(in, subgroupMetadata);
+
+ BluetoothLeBroadcastSourceInfo srcInfo =
+ new BluetoothLeBroadcastSourceInfo(
+ sourceId,
+ sourceAddressType,
+ sourceDevice,
+ sourceAdvSid,
+ broadcastId,
+ paSyncState,
+ encryptionStatus,
+ audioSyncState,
+ badBroadcastCode,
+ numSubGroups,
+ subgroupBisSyncState,
+ subgroupMetadata,
+ broadcastCode);
+ return srcInfo;
+ }
+
+ public @NonNull BluetoothLeBroadcastSourceInfo[] newArray(int size) {
+ return new BluetoothLeBroadcastSourceInfo[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeByte(mSourceId);
+ out.writeInt(mSourceAddressType);
+ out.writeTypedObject(mSourceDevice, 0);
+ out.writeByte(mSourceAdvSid);
+ out.writeInt(mBroadcastId);
+ out.writeInt(mPaSyncState);
+ out.writeInt(mAudioSyncState);
+ out.writeInt(mEncryptionStatus);
+
+ if (mBadBroadcastCode != null) {
+ out.writeInt(mBadBroadcastCode.length);
+ out.writeByteArray(mBadBroadcastCode);
+ } else {
+ // zero indicates that there is no "bad broadcast code"
+ out.writeInt(0);
+ }
+ out.writeByte(mNumSubGroups);
+ out.writeString(mBroadcastCode);
+ writeSubgroupBisSyncStateToParcel(out, mSubgroupBisSyncState);
+ writeSubgroupMetadataToParcel(out, mSubgroupMetadata);
+ }
+
+ private static void log(@NonNull String msg) {
+ if (DBG) {
+ Log.d(TAG, msg);
+ }
+ }
+}
+;
diff --git a/core/java/android/bluetooth/BluetoothLeCall.java b/core/java/android/bluetooth/BluetoothLeCall.java
new file mode 100644
index 0000000..fb7789d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeCall.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Representation of Call
+ *
+ * @hide
+ */
+public final class BluetoothLeCall implements Parcelable {
+
+ /** @hide */
+ @IntDef(prefix = "STATE_", value = {
+ STATE_INCOMING,
+ STATE_DIALING,
+ STATE_ALERTING,
+ STATE_ACTIVE,
+ STATE_LOCALLY_HELD,
+ STATE_REMOTELY_HELD,
+ STATE_LOCALLY_AND_REMOTELY_HELD
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {
+ }
+
+ /**
+ * A remote party is calling (incoming call).
+ *
+ * @hide
+ */
+ public static final int STATE_INCOMING = 0x00;
+
+ /**
+ * The process to call the remote party has started but the remote party is not
+ * being alerted (outgoing call).
+ *
+ * @hide
+ */
+ public static final int STATE_DIALING = 0x01;
+
+ /**
+ * A remote party is being alerted (outgoing call).
+ *
+ * @hide
+ */
+ public static final int STATE_ALERTING = 0x02;
+
+ /**
+ * The call is in an active conversation.
+ *
+ * @hide
+ */
+ public static final int STATE_ACTIVE = 0x03;
+
+ /**
+ * The call is connected but held locally. “Locally Held” implies that either
+ * the server or the client can affect the state.
+ *
+ * @hide
+ */
+ public static final int STATE_LOCALLY_HELD = 0x04;
+
+ /**
+ * The call is connected but held remotely. “Remotely Held” means that the state
+ * is controlled by the remote party of a call.
+ *
+ * @hide
+ */
+ public static final int STATE_REMOTELY_HELD = 0x05;
+
+ /**
+ * The call is connected but held both locally and remotely.
+ *
+ * @hide
+ */
+ public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06;
+
+ /**
+ * Whether the call direction is outgoing.
+ *
+ * @hide
+ */
+ public static final int FLAG_OUTGOING_CALL = 0x00000001;
+
+ /**
+ * Whether the call URI and Friendly Name are withheld by server.
+ *
+ * @hide
+ */
+ public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002;
+
+ /**
+ * Whether the call URI and Friendly Name are withheld by network.
+ *
+ * @hide
+ */
+ public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004;
+
+ /** Unique UUID that identifies this call */
+ private UUID mUuid;
+
+ /** Remote Caller URI */
+ private String mUri;
+
+ /** Caller friendly name */
+ private String mFriendlyName;
+
+ /** Call state */
+ private @State int mState;
+
+ /** Call flags */
+ private int mCallFlags;
+
+ /** @hide */
+ public BluetoothLeCall(@NonNull BluetoothLeCall that) {
+ mUuid = new UUID(that.getUuid().getMostSignificantBits(),
+ that.getUuid().getLeastSignificantBits());
+ mUri = that.mUri;
+ mFriendlyName = that.mFriendlyName;
+ mState = that.mState;
+ mCallFlags = that.mCallFlags;
+ }
+
+ /** @hide */
+ public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName,
+ @State int state, int callFlags) {
+ mUuid = uuid;
+ mUri = uri;
+ mFriendlyName = friendlyName;
+ mState = state;
+ mCallFlags = callFlags;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ BluetoothLeCall that = (BluetoothLeCall) o;
+ return mUuid.equals(that.mUuid) && mUri.equals(that.mUri)
+ && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState
+ && mCallFlags == that.mCallFlags;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags);
+ }
+
+ /**
+ * Returns a string representation of this BluetoothLeCall.
+ *
+ * <p>
+ * Currently this is the UUID.
+ *
+ * @return string representation of this BluetoothLeCall
+ */
+ @Override
+ public String toString() {
+ return mUuid.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeParcelable(new ParcelUuid(mUuid), 0);
+ out.writeString(mUri);
+ out.writeString(mFriendlyName);
+ out.writeInt(mState);
+ out.writeInt(mCallFlags);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeCall> CREATOR =
+ new Parcelable.Creator<BluetoothLeCall>() {
+ public BluetoothLeCall createFromParcel(Parcel in) {
+ return new BluetoothLeCall(in);
+ }
+
+ public BluetoothLeCall[] newArray(int size) {
+ return new BluetoothLeCall[size];
+ }
+ };
+
+ private BluetoothLeCall(Parcel in) {
+ mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
+ mUri = in.readString();
+ mFriendlyName = in.readString();
+ mState = in.readInt();
+ mCallFlags = in.readInt();
+ }
+
+ /**
+ * Returns an UUID of this BluetoothLeCall.
+ *
+ * <p>
+ * An UUID is unique identifier of a BluetoothLeCall.
+ *
+ * @return UUID of this BluetoothLeCall
+ * @hide
+ */
+ public @NonNull UUID getUuid() {
+ return mUuid;
+ }
+
+ /**
+ * Returns a URI of the remote party of this BluetoothLeCall.
+ *
+ * @return string representation of this BluetoothLeCall
+ * @hide
+ */
+ public @NonNull String getUri() {
+ return mUri;
+ }
+
+ /**
+ * Returns a friendly name of the call.
+ *
+ * @return friendly name representation of this BluetoothLeCall
+ * @hide
+ */
+ public @NonNull String getFriendlyName() {
+ return mFriendlyName;
+ }
+
+ /**
+ * Returns the call state.
+ *
+ * @return the state of this BluetoothLeCall
+ * @hide
+ */
+ public @State int getState() {
+ return mState;
+ }
+
+ /**
+ * Returns the call flags.
+ *
+ * @return call flags
+ * @hide
+ */
+ public int getCallFlags() {
+ return mCallFlags;
+ }
+
+ /**
+ * Whether the call direction is incoming.
+ *
+ * @return true if incoming call, false otherwise
+ * @hide
+ */
+ public boolean isIncomingCall() {
+ return (mCallFlags & FLAG_OUTGOING_CALL) == 0;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothLeCallControl.java b/core/java/android/bluetooth/BluetoothLeCallControl.java
new file mode 100644
index 0000000..5283e08
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeCallControl.java
@@ -0,0 +1,911 @@
+/*
+ * Copyright 2019 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.Log;
+import android.annotation.SuppressLint;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides the APIs to control the Call Control profile.
+ *
+ * <p>
+ * This class provides Bluetooth Telephone Bearer Service functionality,
+ * allowing applications to expose a GATT Service based interface to control the
+ * state of the calls by remote devices such as LE audio devices.
+ *
+ * <p>
+ * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
+ * BluetoothLeCallControl proxy object.
+ *
+ * @hide
+ */
+public final class BluetoothLeCallControl implements BluetoothProfile {
+ private static final String TAG = "BluetoothLeCallControl";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+
+ /** @hide */
+ @IntDef(prefix = "RESULT_", value = {
+ RESULT_SUCCESS,
+ RESULT_ERROR_UNKNOWN_CALL_ID,
+ RESULT_ERROR_INVALID_URI,
+ RESULT_ERROR_APPLICATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Result {
+ }
+
+ /**
+ * Opcode write was successful.
+ *
+ * @hide
+ */
+ public static final int RESULT_SUCCESS = 0;
+
+ /**
+ * Unknown call Id has been used in the operation.
+ *
+ * @hide
+ */
+ public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1;
+
+ /**
+ * The URI provided in {@link Callback#onPlaceCallRequest} is invalid.
+ *
+ * @hide
+ */
+ public static final int RESULT_ERROR_INVALID_URI = 2;
+
+ /**
+ * Application internal error.
+ *
+ * @hide
+ */
+ public static final int RESULT_ERROR_APPLICATION = 3;
+
+ /** @hide */
+ @IntDef(prefix = "TERMINATION_REASON_", value = {
+ TERMINATION_REASON_INVALID_URI,
+ TERMINATION_REASON_FAIL,
+ TERMINATION_REASON_REMOTE_HANGUP,
+ TERMINATION_REASON_SERVER_HANGUP,
+ TERMINATION_REASON_LINE_BUSY,
+ TERMINATION_REASON_NETWORK_CONGESTION,
+ TERMINATION_REASON_CLIENT_HANGUP,
+ TERMINATION_REASON_NO_SERVICE,
+ TERMINATION_REASON_NO_ANSWER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TerminationReason {
+ }
+
+ /**
+ * Remote Caller ID value used to place a call was formed improperly.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_INVALID_URI = 0x00;
+
+ /**
+ * Call fail.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_FAIL = 0x01;
+
+ /**
+ * Remote party ended call.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02;
+
+ /**
+ * Call ended from the server.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03;
+
+ /**
+ * Line busy.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_LINE_BUSY = 0x04;
+
+ /**
+ * Network congestion.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05;
+
+ /**
+ * Client terminated.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06;
+
+ /**
+ * No service.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_NO_SERVICE = 0x07;
+
+ /**
+ * No answer.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_NO_ANSWER = 0x08;
+
+ /*
+ * Flag indicating support for hold/unhold call feature.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_HOLD_CALL = 0x00000001;
+
+ /**
+ * Flag indicating support for joining calls feature.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_JOIN_CALLS = 0x00000002;
+
+ private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102;
+ private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103;
+
+ private static final int REG_TIMEOUT = 10000;
+
+ /**
+ * The template class is used to call callback functions on events from the TBS
+ * server. Callback functions are wrapped in this class and registered to the
+ * Android system during app registration.
+ *
+ * @hide
+ */
+ public abstract static class Callback {
+
+ private static final String TAG = "BluetoothLeCallControl.Callback";
+
+ /**
+ * Called when a remote client requested to accept the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to be accepted
+ * @hide
+ */
+ public abstract void onAcceptCall(int requestId, @NonNull UUID callId);
+
+ /**
+ * A remote client has requested to terminate the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to terminate
+ * @hide
+ */
+ public abstract void onTerminateCall(int requestId, @NonNull UUID callId);
+
+ /**
+ * A remote client has requested to hold the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to be put on hold
+ * @hide
+ */
+ public void onHoldCall(int requestId, @NonNull UUID callId) {
+ Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
+ }
+
+ /**
+ * A remote client has requested to unhold the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to unhold
+ * @hide
+ */
+ public void onUnholdCall(int requestId, @NonNull UUID callId) {
+ Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
+ }
+
+ /**
+ * A remote client has requested to place a call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The Id to be assigned for the new call
+ * @param uri The caller URI requested
+ * @hide
+ */
+ public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri);
+
+ /**
+ * A remote client has requested to join the calls.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callIds The call Id list requested to join
+ * @hide
+ */
+ public void onJoinCalls(int requestId, @NonNull List<UUID> callIds) {
+ Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!");
+ }
+ }
+
+ private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub {
+
+ private final Executor mExecutor;
+ private final Callback mCallback;
+
+ CallbackWrapper(Executor executor, Callback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onBearerRegistered(int ccid) {
+ synchronized (mServerIfLock) {
+ if (mCallback != null) {
+ mCcid = ccid;
+ mServerIfLock.notifyAll();
+ } else {
+ // registration timeout
+ Log.e(TAG, "onBearerRegistered: mCallback is null");
+ }
+ }
+ }
+
+ @Override
+ public void onAcceptCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onTerminateCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onHoldCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onUnholdCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onJoinCalls(int requestId, List<ParcelUuid> parcelUuids) {
+ List<UUID> uuids = new ArrayList<>();
+ for (ParcelUuid parcelUuid : parcelUuids) {
+ uuids.add(parcelUuid.getUuid());
+ }
+
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+ };
+
+ private Context mContext;
+ private ServiceListener mServiceListener;
+ private volatile IBluetoothLeCallControl mService;
+ private BluetoothAdapter mAdapter;
+ private int mCcid = 0;
+ private String mToken;
+ private Callback mCallback = null;
+ private Object mServerIfLock = new Object();
+
+ private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG)
+ Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ doUnbind();
+ } else {
+ doBind();
+ }
+ }
+ };
+
+ /**
+ * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth
+ * telephone bearer service.
+ */
+ /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) {
+ mContext = context;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mServiceListener = listener;
+
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ doBind();
+ }
+
+ private boolean doBind() {
+ synchronized (mConnection) {
+ if (mService == null) {
+ if (VDBG)
+ Log.d(TAG, "Binding service...");
+ try {
+ return mAdapter.getBluetoothManager().
+ bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
+ mConnection);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to bind TelephoneBearerService", e);
+ }
+ }
+ }
+ return false;
+ }
+
+ private void doUnbind() {
+ synchronized (mConnection) {
+ if (mService != null) {
+ if (VDBG)
+ Log.d(TAG, "Unbinding service...");
+ try {
+ mAdapter.getBluetoothManager().
+ unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
+ mConnection);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to unbind TelephoneBearerService", e);
+ } finally {
+ mService = null;
+ }
+ }
+ }
+ }
+
+ /* package */ void close() {
+ if (VDBG)
+ log("close()");
+ unregisterBearer();
+
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "", re);
+ }
+ }
+ mServiceListener = null;
+ doUnbind();
+ }
+
+ private IBluetoothLeCallControl getService() {
+ return mService;
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ public int getConnectionState(@Nullable BluetoothDevice device) {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ @NonNull int[] states) {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ /**
+ * Register Telephone Bearer exposing the interface that allows remote devices
+ * to track and control the call states.
+ *
+ * <p>
+ * This is an asynchronous call. The callback is used to notify success or
+ * failure if the function returns true.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * <!-- The UCI is a String identifier of the telephone bearer as defined at
+ * https://www.bluetooth.com/specifications/assigned-numbers/uniform-caller-identifiers
+ * (login required). -->
+ *
+ * <!-- The examples of common URI schemes can be found in
+ * https://iana.org/assignments/uri-schemes/uri-schemes.xhtml -->
+ *
+ * <!-- The Technology is an integer value. The possible values are defined at
+ * https://www.bluetooth.com/specifications/assigned-numbers (login required).
+ * -->
+ *
+ * @param uci Bearer Unique Client Identifier
+ * @param uriSchemes URI Schemes supported list
+ * @param capabilities bearer capabilities
+ * @param provider Network provider name
+ * @param technology Network technology
+ * @param executor {@link Executor} object on which callback will be
+ * executed. The Executor object is required.
+ * @param callback {@link Callback} object to which callback messages will
+ * be sent. The Callback object is required.
+ * @return true on success, false otherwise
+ * @hide
+ */
+ @SuppressLint("ExecutorRegistration")
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean registerBearer(@Nullable String uci,
+ @NonNull List<String> uriSchemes, int capabilities,
+ @NonNull String provider, int technology,
+ @NonNull Executor executor, @NonNull Callback callback) {
+ if (DBG) {
+ Log.d(TAG, "registerBearer");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("null parameter: " + callback);
+ }
+ if (mCcid != 0) {
+ return false;
+ }
+
+ mToken = uci;
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ synchronized (mServerIfLock) {
+ if (mCallback != null) {
+ Log.e(TAG, "Bearer can be opened only once");
+ return false;
+ }
+
+ mCallback = callback;
+ try {
+ CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback);
+ service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities,
+ provider, technology);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ mCallback = null;
+ return false;
+ }
+
+ try {
+ mServerIfLock.wait(REG_TIMEOUT);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "" + e);
+ mCallback = null;
+ }
+
+ if (mCcid == 0) {
+ mCallback = null;
+ return false;
+ }
+
+ return true;
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return false;
+ }
+
+ /**
+ * Unregister Telephone Bearer Service and destroy all the associated data.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void unregisterBearer() {
+ if (DBG) {
+ Log.d(TAG, "unregisterBearer");
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ int ccid = mCcid;
+ mCcid = 0;
+ mCallback = null;
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.unregisterBearer(mToken);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Get the Content Control ID (CCID) value.
+ *
+ * @return ccid Content Control ID value
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public int getContentControlId() {
+ return mCcid;
+ }
+
+ /**
+ * Notify about the newly added call.
+ *
+ * <p>
+ * This shall be called as early as possible after the call has been added.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param call Newly added call
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void onCallAdded(@NonNull BluetoothLeCall call) {
+ if (DBG) {
+ Log.d(TAG, "onCallAdded: call=" + call);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.callAdded(mCcid, call);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Notify about the removed call.
+ *
+ * <p>
+ * This shall be called as early as possible after the call has been removed.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callId The Id of a call that has been removed
+ * @param reason Call termination reason
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) {
+ if (DBG) {
+ Log.d(TAG, "callRemoved: callId=" + callId);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.callRemoved(mCcid, new ParcelUuid(callId), reason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Notify the call state change
+ *
+ * <p>
+ * This shall be called as early as possible after the state of the call has
+ * changed.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callId The call Id that state has been changed
+ * @param state Call state
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) {
+ if (DBG) {
+ Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.callStateChanged(mCcid, new ParcelUuid(callId), state);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Provide the current calls list
+ *
+ * <p>
+ * This function must be invoked after registration if application has any
+ * calls.
+ *
+ * @param calls current calls list
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void currentCallsList(@NonNull List<BluetoothLeCall> calls) {
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.currentCallsList(mCcid, calls);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ }
+
+ /**
+ * Provide the network current status
+ *
+ * <p>
+ * This function must be invoked on change of network state.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * <!-- The Technology is an integer value. The possible values are defined at
+ * https://www.bluetooth.com/specifications/assigned-numbers (login required).
+ * -->
+ *
+ * @param provider Network provider name
+ * @param technology Network technology
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void networkStateChanged(@NonNull String provider, int technology) {
+ if (DBG) {
+ Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.networkStateChanged(mCcid, provider, technology);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Send a response to a call control request to a remote device.
+ *
+ * <p>
+ * This function must be invoked in when a request is received by one of these
+ * callback methods:
+ *
+ * <ul>
+ * <li>{@link Callback#onAcceptCall}
+ * <li>{@link Callback#onTerminateCall}
+ * <li>{@link Callback#onHoldCall}
+ * <li>{@link Callback#onUnholdCall}
+ * <li>{@link Callback#onPlaceCall}
+ * <li>{@link Callback#onJoinCalls}
+ * </ul>
+ *
+ * @param requestId The ID of the request that was received with the callback
+ * @param result The result of the request to be sent to the remote devices
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void requestResult(int requestId, @Result int result) {
+ if (DBG) {
+ Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.requestResult(mCcid, requestId, result);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ }
+
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+
+ private final IBluetoothProfileServiceConnection mConnection =
+ new IBluetoothProfileServiceConnection.Stub() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) {
+ Log.d(TAG, "Proxy object connected");
+ }
+ mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service));
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) {
+ Log.d(TAG, "Proxy object disconnected");
+ }
+ doUnbind();
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED));
+ }
+ };
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_TBS_SERVICE_CONNECTED: {
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL,
+ BluetoothLeCallControl.this);
+ }
+ break;
+ }
+ case MESSAGE_TBS_SERVICE_DISCONNECTED: {
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL);
+ }
+ break;
+ }
+ }
+ }
+ };
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index e047e5d..d0f74e9 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -240,12 +240,19 @@
int LE_AUDIO_BROADCAST = 26;
/**
+ * @hide
+ * Telephone Bearer Service from Call Control Profile
+ *
+ */
+ int LE_CALL_CONTROL = 27;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 26;
+ int MAX_PROFILE_ID = 27;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 120a0a6..bccfacf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5922,6 +5922,7 @@
* @see android.nearby.NearbyManager
* @hide
*/
+ @SystemApi
public static final String NEARBY_SERVICE = "nearby";
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2ff29cb..af84392 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3794,6 +3794,47 @@
"android.intent.action.ACTION_IDLE_MAINTENANCE_END";
/**
+ * Broadcast Action: A broadcast sent by the system to indicate that
+ * {@link android.safetycenter.SafetyCenterManager} is requesting data from safety sources
+ * regarding their safety state.
+ *
+ * This broadcast is sent when a user triggers a data refresh from the Safety Center UI or when
+ * Safety Center detects that its stored safety information is stale and needs to be updated.
+ *
+ * This broadcast is sent explicitly to safety sources by targeting intents to a specified set
+ * of components provided by the safety sources in the safety source configuration.
+ * The receiving components should be manifest-declared receivers so that safety sources can be
+ * requested to send data even if they are not running.
+ *
+ * On receiving this broadcast, safety sources should determine their safety state
+ * according to the parameters specified in the intent extras (see below) and send Safety Center
+ * data about their safety state using
+ * {@link android.safetycenter.SafetyCenterManager#sendSafetyCenterUpdate(android.safetycenter.SafetySourceData)}.
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system.
+ *
+ * <p>Includes the following extras:
+ * <ul>
+ * <li>{@link #EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE}: An int representing the type of data
+ * being requested. Possible values are all values in {@link RefreshRequestType}.
+ * <li>{@link #EXTRA_REFRESH_SAFETY_SOURCE_IDS}: A {@code String[]} of ids
+ * representing the safety sources being requested for data. This extra exists for
+ * disambiguation in the case that a single component is responsible for receiving refresh
+ * requests for multiple safety sources.
+ * </ul>
+ *
+ * @hide
+ */
+ // TODO(b/210805082): Define the term "safety sources" more concretely here once safety sources
+ // are configured in xml config.
+ // TODO(b/210979035): Determine recommendation for sources if they are requested for fresh data
+ // but cannot provide it.
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_REFRESH_SAFETY_SOURCES =
+ "android.intent.action.REFRESH_SAFETY_SOURCES";
+
+ /**
* Broadcast Action: a remote intent is to be broadcasted.
*
* A remote intent is used for remote RPC between devices. The remote intent
@@ -6386,6 +6427,77 @@
public static final String EXTRA_VISIBILITY_ALLOW_LIST =
"android.intent.extra.VISIBILITY_ALLOW_LIST";
+
+ /**
+ * Used as a {@code String[]} extra field in
+ * {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES} intents to specify the safety
+ * source ids of the safety sources being requested for data by Safety Center.
+ *
+ * When this extra field is not specified in the intent, it is assumed that Safety Center is
+ * requesting data from all safety sources supported by the component receiving the broadcast.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_REFRESH_SAFETY_SOURCE_IDS =
+ "android.intent.extra.REFRESH_SAFETY_SOURCE_IDS";
+
+ /**
+ * Used as an {@code int} extra field in
+ * {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES} intents to specify the type of
+ * data request from Safety Center.
+ *
+ * Possible values are all values in {@link RefreshRequestType}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE =
+ "android.intent.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE";
+
+ /**
+ * All possible types of data refresh requests in broadcasts with intent action
+ * {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES}.
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "EXTRA_REFRESH_REQUEST_TYPE_" }, value = {
+ EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA,
+ EXTRA_REFRESH_REQUEST_TYPE_GET_DATA,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RefreshRequestType {}
+
+ /**
+ * Used as an int value for
+ * {@link android.content.Intent#EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE}
+ * to indicate that the safety source should fetch fresh data relating to their safety state
+ * upon receiving a broadcast with intent action
+ * {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES} and provide it to Safety Center.
+ *
+ * The term "fresh" here means that the sources should ensure that the safety data is accurate
+ * as possible at the time of providing it to Safety Center, even if it involves performing an
+ * expensive and/or slow process.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA = 0;
+
+ /**
+ * Used as an int value for
+ * {@link android.content.Intent#EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE}
+ * to indicate that upon receiving a broadcasts with intent action
+ * {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES}, the safety source should
+ * provide data relating to their safety state to Safety Center.
+ *
+ * If the source already has its safety data cached, it may provide it without triggering a
+ * process to fetch state which may be expensive and/or slow.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EXTRA_REFRESH_REQUEST_TYPE_GET_DATA = 1;
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 37fd3ff..cb8988e 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -38,6 +38,8 @@
import android.os.UserHandle;
import android.os.ParcelFileDescriptor;
+import com.android.internal.infra.AndroidFuture;
+
import java.util.List;
/**
@@ -73,6 +75,8 @@
ParceledListSlice getShortcuts(String callingPackage, in ShortcutQueryWrapper query,
in UserHandle user);
+ void getShortcutsAsync(String callingPackage, in ShortcutQueryWrapper query,
+ in UserHandle user, in AndroidFuture<List<ShortcutInfo>> cb);
void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
in UserHandle user);
boolean startShortcut(String callingPackage, String packageName, String featureId, String id,
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 617d3ab..a0d348f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -69,6 +69,7 @@
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileNotFoundException;
@@ -84,6 +85,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
/**
@@ -440,6 +442,17 @@
public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
/**
+ * Includes shortcuts from persistence layer in the search result.
+ *
+ * <p>The caller should make the query on a worker thread since accessing persistence layer
+ * is considered asynchronous.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAG_GET_PERSISTED_DATA = 1 << 12;
+
+ /**
* Populate the persons field in the result. See {@link ShortcutInfo#getPersons()}.
*
* <p>The caller must have the system {@code ACCESS_SHORTCUTS} permission.
@@ -459,6 +472,7 @@
FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
FLAG_GET_KEY_FIELDS_ONLY,
FLAG_GET_PERSONS_DATA,
+ FLAG_GET_PERSISTED_DATA
})
@Retention(RetentionPolicy.SOURCE)
public @interface QueryFlags {}
@@ -1137,6 +1151,9 @@
@NonNull UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
+ if ((query.mQueryFlags & ShortcutQuery.FLAG_GET_PERSISTED_DATA) != 0) {
+ return getShortcutsBlocked(query, user);
+ }
// Note this is the only case we need to update the disabled message for shortcuts
// that weren't restored.
// The restore problem messages are only shown by the user, and publishers will never
@@ -1144,13 +1161,29 @@
// changed callback, but that only returns shortcuts with the "key" information, so
// that won't return disabled message.
return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(),
- new ShortcutQueryWrapper(query), user)
- .getList());
+ new ShortcutQueryWrapper(query), user)
+ .getList());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ private List<ShortcutInfo> getShortcutsBlocked(@NonNull ShortcutQuery query,
+ @NonNull UserHandle user) {
+ logErrorForInvalidProfileAccess(user);
+ final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
+ future.thenApply(this::maybeUpdateDisabledMessage);
+ try {
+ mService.getShortcutsAsync(mContext.getPackageName(),
+ new ShortcutQueryWrapper(query), user, future);
+ return future.get();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
/**
* @hide // No longer used. Use getShortcuts() instead. Kept for unit tests.
*/
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 251d5e8..495100b 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2374,7 +2374,7 @@
STAGED_SESSION_UNKNOWN,
STAGED_SESSION_CONFLICT})
@Retention(RetentionPolicy.SOURCE)
- public @interface StagedSessionErrorCode{}
+ public @interface SessionErrorCode {}
/**
* Constant indicating that no error occurred during the preparation or the activation of
* this staged session.
@@ -2486,13 +2486,13 @@
public int[] childSessionIds = NO_SESSIONS;
/** {@hide} */
- public boolean isStagedSessionApplied;
+ public boolean isSessionApplied;
/** {@hide} */
- public boolean isStagedSessionReady;
+ public boolean isSessionReady;
/** {@hide} */
- public boolean isStagedSessionFailed;
- private int mStagedSessionErrorCode;
- private String mStagedSessionErrorMessage;
+ public boolean isSessionFailed;
+ private int mSessionErrorCode;
+ private String mSessionErrorMessage;
/** {@hide} */
public boolean isCommitted;
@@ -2553,11 +2553,11 @@
if (childSessionIds == null) {
childSessionIds = NO_SESSIONS;
}
- isStagedSessionApplied = source.readBoolean();
- isStagedSessionReady = source.readBoolean();
- isStagedSessionFailed = source.readBoolean();
- mStagedSessionErrorCode = source.readInt();
- mStagedSessionErrorMessage = source.readString();
+ isSessionApplied = source.readBoolean();
+ isSessionReady = source.readBoolean();
+ isSessionFailed = source.readBoolean();
+ mSessionErrorCode = source.readInt();
+ mSessionErrorMessage = source.readString();
isCommitted = source.readBoolean();
rollbackDataPolicy = source.readInt();
createdMillis = source.readLong();
@@ -2951,7 +2951,7 @@
* since that is the one that should have been {@link Session#commit committed}.
*/
public boolean isStagedSessionActive() {
- return isStaged && isCommitted && !isStagedSessionApplied && !isStagedSessionFailed
+ return isStaged && isCommitted && !isSessionApplied && !isSessionFailed
&& !hasParentSessionId();
}
@@ -2992,7 +2992,7 @@
*/
public boolean isStagedSessionApplied() {
checkSessionIsStaged();
- return isStagedSessionApplied;
+ return isSessionApplied;
}
/**
@@ -3001,7 +3001,7 @@
*/
public boolean isStagedSessionReady() {
checkSessionIsStaged();
- return isStagedSessionReady;
+ return isSessionReady;
}
/**
@@ -3010,16 +3010,16 @@
*/
public boolean isStagedSessionFailed() {
checkSessionIsStaged();
- return isStagedSessionFailed;
+ return isSessionFailed;
}
/**
* If something went wrong with a staged session, clients can check this error code to
* understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
*/
- public @StagedSessionErrorCode int getStagedSessionErrorCode() {
+ public @SessionErrorCode int getStagedSessionErrorCode() {
checkSessionIsStaged();
- return mStagedSessionErrorCode;
+ return mSessionErrorCode;
}
/**
@@ -3028,14 +3028,13 @@
*/
public @NonNull String getStagedSessionErrorMessage() {
checkSessionIsStaged();
- return mStagedSessionErrorMessage;
+ return mSessionErrorMessage;
}
/** {@hide} */
- public void setStagedSessionErrorCode(@StagedSessionErrorCode int errorCode,
- String errorMessage) {
- mStagedSessionErrorCode = errorCode;
- mStagedSessionErrorMessage = errorMessage;
+ public void setSessionErrorCode(@SessionErrorCode int errorCode, String errorMessage) {
+ mSessionErrorCode = errorCode;
+ mSessionErrorMessage = errorMessage;
}
/**
@@ -3124,11 +3123,11 @@
dest.writeBoolean(forceQueryable);
dest.writeInt(parentSessionId);
dest.writeIntArray(childSessionIds);
- dest.writeBoolean(isStagedSessionApplied);
- dest.writeBoolean(isStagedSessionReady);
- dest.writeBoolean(isStagedSessionFailed);
- dest.writeInt(mStagedSessionErrorCode);
- dest.writeString(mStagedSessionErrorMessage);
+ dest.writeBoolean(isSessionApplied);
+ dest.writeBoolean(isSessionReady);
+ dest.writeBoolean(isSessionFailed);
+ dest.writeInt(mSessionErrorCode);
+ dest.writeString(mSessionErrorMessage);
dest.writeBoolean(isCommitted);
dest.writeInt(rollbackDataPolicy);
dest.writeLong(createdMillis);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9a7aeef..338dfd6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -145,6 +145,20 @@
"android.media.PROPERTY_MEDIA_CAPABILITIES";
/**
+ * Application level property that an app can specify to opt-out from having private data
+ * directories both on the internal and external storages.
+ *
+ * <p>Changing the value of this property during app update is not supported, and such updates
+ * will be rejected.
+ *
+ * <p>This should only be set by platform apps that know what they are doing.
+ *
+ * @hide
+ */
+ public static final String PROPERTY_NO_APP_DATA_STORAGE =
+ "android.internal.PROPERTY_NO_APP_DATA_STORAGE";
+
+ /**
* A property value set within the manifest.
* <p>
* The value of a property will only have a single type, as defined by
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 3ed5c64..087a795 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -29,6 +29,8 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import com.android.internal.infra.AndroidFuture;
+
import java.util.List;
/**
@@ -50,6 +52,19 @@
@Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
@ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid);
+ /**
+ * Retrieves shortcuts asynchronously. Query will go through persistence layer (thus making the
+ * call async) if querying by shortcutIds in a specific package; otherwise it's effectively the
+ * same as calling {@link #getShortcuts}.
+ */
+ public abstract void
+ getShortcutsAsync(int launcherUserId,
+ @NonNull String callingPackage, long changedSince,
+ @Nullable String packageName, @Nullable List<String> shortcutIds,
+ @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
+ @ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid,
+ AndroidFuture<List<ShortcutInfo>> cb);
+
public abstract boolean
isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String id, int userId);
@@ -63,6 +78,14 @@
@NonNull String packageName, @NonNull String shortcutId, int userId,
int callingPid, int callingUid);
+ /**
+ * Retrieves the intents from a specified shortcut asynchronously.
+ */
+ public abstract void createShortcutIntentsAsync(
+ int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId,
+ int callingPid, int callingUid, @NonNull AndroidFuture<Intent[]> cb);
+
public abstract void addListener(@NonNull ShortcutChangeListener listener);
public abstract void addShortcutChangeCallback(
@@ -82,6 +105,13 @@
@NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
+ /**
+ * Retrieves a file descriptor from the icon in a specified shortcut asynchronously.
+ */
+ public abstract void getShortcutIconFdAsync(int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId,
+ @NonNull AndroidFuture<ParcelFileDescriptor> cb);
+
public abstract boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage, int callingPid, int callingUid);
@@ -117,6 +147,14 @@
public abstract String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
+ /**
+ * Retrieves the icon Uri of the shortcut asynchronously, and grants Uri read permission to the
+ * caller.
+ */
+ public abstract void getShortcutIconUriAsync(int launcherUserId,
+ @NonNull String launcherPackage, @NonNull String packageName,
+ @NonNull String shortcutId, int userId, @NonNull AndroidFuture<String> cb);
+
public abstract boolean isSharingShortcut(int callingUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId,
@NonNull IntentFilter filter);
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 26f0826..77b8be3 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -59,6 +59,72 @@
"include-filter": "android.content.pm.cts"
}
]
+ },
+ {
+ "name": "CtsUsesNativeLibraryTest",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsAppSearchHostTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsSilentUpdateHostTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsSuspendAppsTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsSecureFrpInstallTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsSuspendAppsPermissionTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 16deaa0..f336672 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -21,6 +21,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
@@ -934,6 +935,13 @@
);
}
+ if (ParsedPermissionUtils.declareDuplicatePermission(pkg)) {
+ return input.error(
+ INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Declare duplicate permissions with different protection levels."
+ );
+ }
+
convertCompatPermissions(pkg);
convertSplitPermissions(pkg);
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index 66e9d3d..86c8f02 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -27,6 +27,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.R;
@@ -34,6 +35,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.List;
/** @hide */
public class ParsedPermissionUtils {
@@ -271,4 +273,29 @@
}
return size;
}
+
+ /**
+ * @return {@code true} if the package declares duplicate permissions with different
+ * protection levels.
+ */
+ public static boolean declareDuplicatePermission(@NonNull ParsingPackage pkg) {
+ final List<ParsedPermission> permissions = pkg.getPermissions();
+ final int size = permissions.size();
+ if (size > 0) {
+ final ArrayMap<String, ParsedPermission> checkDuplicatePerm = new ArrayMap<>(size);
+ for (int i = 0; i < size; i++) {
+ final ParsedPermission parsedPermission = permissions.get(i);
+ final String name = parsedPermission.getName();
+ final ParsedPermission perm = checkDuplicatePerm.get(name);
+ // Since a permission tree is also added as a permission with normal protection
+ // level, we need to skip if the parsedPermission is a permission tree.
+ if (perm != null && !(perm.isTree() || parsedPermission.isTree())
+ && perm.getProtectionLevel() != parsedPermission.getProtectionLevel()) {
+ return true;
+ }
+ checkDuplicatePerm.put(name, parsedPermission);
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 01833fd..e731165 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -965,7 +965,7 @@
private static final class DisplayListenerDelegate extends Handler {
public final DisplayListener mListener;
- public long mEventsMask;
+ public volatile long mEventsMask;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
@@ -985,12 +985,12 @@
removeCallbacksAndMessages(null);
}
- public synchronized void setEventsMask(@EventsMask long newEventsMask) {
+ public void setEventsMask(@EventsMask long newEventsMask) {
mEventsMask = newEventsMask;
}
@Override
- public synchronized void handleMessage(Message msg) {
+ public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_DISPLAY_ADDED:
if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 66288d6..6c8eb41 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -17,11 +17,7 @@
package android.inputmethodservice;
import static android.inputmethodservice.SoftInputWindowProto.BOUNDS;
-import static android.inputmethodservice.SoftInputWindowProto.GRAVITY;
-import static android.inputmethodservice.SoftInputWindowProto.NAME;
-import static android.inputmethodservice.SoftInputWindowProto.TAKES_FOCUS;
import static android.inputmethodservice.SoftInputWindowProto.WINDOW_STATE;
-import static android.inputmethodservice.SoftInputWindowProto.WINDOW_TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -33,7 +29,6 @@
import android.os.IBinder;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
-import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -268,11 +263,6 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- // TODO(b/192412909): Deprecate the following 4 entries, as they are all constant.
- proto.write(NAME, "InputMethod");
- proto.write(WINDOW_TYPE, WindowManager.LayoutParams.TYPE_INPUT_METHOD);
- proto.write(GRAVITY, Gravity.BOTTOM);
- proto.write(TAKES_FOCUS, false);
mBounds.dumpDebug(proto, BOUNDS);
proto.write(WINDOW_STATE, mWindowState);
proto.end(token);
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index f4b427959f..596f431 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -339,7 +339,8 @@
out.writeInt(template.getMatchRule());
BackupUtils.writeString(out, template.getSubscriberIds().iterator().next());
- BackupUtils.writeString(out, template.getWifiNetworkKey());
+ BackupUtils.writeString(out, template.getWifiNetworkKeys().isEmpty()
+ ? null : template.getWifiNetworkKeys().iterator().next());
out.writeInt(template.getMeteredness());
return baos.toByteArray();
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fa209cc..520730f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3374,6 +3374,11 @@
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
/**
+ * Returns aggregated wake lock stats.
+ */
+ public abstract WakeLockStats getWakeLockStats();
+
+ /**
* Returns Timers tracking the total time of each Resource Power Manager state and voter.
*/
public abstract Map<String, ? extends Timer> getRpmStats();
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 7f526c1..6339435 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -157,6 +157,7 @@
@Retention(RetentionPolicy.SOURCE)
public @interface WifiSupplState {}
+
private final IBatteryStats mBatteryStats;
/** @hide */
@@ -352,6 +353,21 @@
}
/**
+ * Retrieves accumulate wake lock stats.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+ @NonNull
+ public WakeLockStats getWakeLockStats() {
+ try {
+ return mBatteryStats.getWakeLockStats();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Indicates an app acquiring full wifi lock.
*
* @param ws worksource (to be used for battery blaming).
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index a23dae8..abe5f81 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -135,7 +135,7 @@
private final List<UidBatteryConsumer> mUidBatteryConsumers;
private final List<UserBatteryConsumer> mUserBatteryConsumers;
private final AggregateBatteryConsumer[] mAggregateBatteryConsumers;
- private final Parcel mHistoryBuffer;
+ private final BatteryStatsHistory mBatteryStatsHistory;
private CursorWindow mBatteryConsumersCursorWindow;
private BatteryUsageStats(@NonNull Builder builder) {
@@ -146,7 +146,7 @@
mDischargePercentage = builder.mDischargePercentage;
mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah;
mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah;
- mHistoryBuffer = builder.mHistoryBuffer;
+ mBatteryStatsHistory = builder.mBatteryStatsHistory;
mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs;
mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs;
mCustomPowerComponentNames = builder.mCustomPowerComponentNames;
@@ -301,11 +301,11 @@
*/
@NonNull
public BatteryStatsHistoryIterator iterateBatteryStatsHistory() {
- if (mHistoryBuffer == null) {
+ if (mBatteryStatsHistory == null) {
throw new IllegalStateException(
"Battery history was not requested in the BatteryUsageStatsQuery");
}
- return new BatteryStatsHistoryIterator(new BatteryStatsHistory(mHistoryBuffer));
+ return new BatteryStatsHistoryIterator(mBatteryStatsHistory);
}
@Override
@@ -363,12 +363,9 @@
}
if (source.readBoolean()) {
- final byte[] historyBlob = source.readBlob();
-
- mHistoryBuffer = Parcel.obtain();
- mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length);
+ mBatteryStatsHistory = BatteryStatsHistory.createFromBatteryUsageStatsParcel(source);
} else {
- mHistoryBuffer = null;
+ mBatteryStatsHistory = null;
}
}
@@ -389,9 +386,9 @@
mBatteryConsumersCursorWindow.writeToParcel(dest, flags);
- if (mHistoryBuffer != null) {
+ if (mBatteryStatsHistory != null) {
dest.writeBoolean(true);
- dest.writeBlob(mHistoryBuffer.marshall());
+ mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest);
} else {
dest.writeBoolean(false);
}
@@ -770,7 +767,7 @@
new SparseArray<>();
private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
new SparseArray<>();
- private Parcel mHistoryBuffer;
+ private BatteryStatsHistory mBatteryStatsHistory;
public Builder(@NonNull String[] customPowerComponentNames) {
this(customPowerComponentNames, false, false);
@@ -895,8 +892,8 @@
* Sets the parceled recent history.
*/
@NonNull
- public Builder setBatteryHistory(Parcel historyBuffer) {
- mHistoryBuffer = historyBuffer;
+ public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory) {
+ mBatteryStatsHistory = batteryStatsHistory;
return this;
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 1e424d1..5d9f2189 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -10,9 +10,8 @@
# BatteryStats
per-file *BatteryConsumer* = file:/BATTERY_STATS_OWNERS
per-file BatteryManager* = file:/BATTERY_STATS_OWNERS
-per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
-per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS
per-file PowerComponents.java = file:/BATTERY_STATS_OWNERS
+per-file *Stats* = file:/BATTERY_STATS_OWNERS
# Multiuser
per-file IUser* = file:/MULTIUSER_OWNERS
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index dbb5a2c..70aaa5e 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -15,10 +15,13 @@
*/
package android.os;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.ActivityThread;
@@ -2690,6 +2693,12 @@
((AndroidBlockGuardPolicy) policy).onCustomSlowCall(name);
}
+ /** @hide */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static void noteUntaggedSocket() {
+ if (vmUntaggedSocketEnabled()) onUntaggedSocket();
+ }
+
/**
* For code to note that a resource was obtained using a type other than its defined type. This
* is a no-op unless the current thread's {@link android.os.StrictMode.ThreadPolicy} has {@link
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index d8f6344..b9252d6 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -311,7 +311,6 @@
* time or throw.
*
* @throws DateTimeException when no accurate network time can be provided.
- * @hide
*/
public static @NonNull Clock currentNetworkTimeClock() {
return new SimpleClock(ZoneOffset.UTC) {
diff --git a/telephony/java/com/android/internal/telephony/euicc/IResultCallback.aidl b/core/java/android/os/WakeLockStats.aidl
similarity index 76%
rename from telephony/java/com/android/internal/telephony/euicc/IResultCallback.aidl
rename to core/java/android/os/WakeLockStats.aidl
index 69f479c..be08d78 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IResultCallback.aidl
+++ b/core/java/android/os/WakeLockStats.aidl
@@ -13,11 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.internal.telephony.euicc;
-import android.content.Intent;
+package android.os;
-/** @hide */
-oneway interface IResultCallback {
- void onComplete(int resultCode, in Intent resultIntent);
-}
+/** {@hide} */
+parcelable WakeLockStats;
diff --git a/core/java/android/os/WakeLockStats.java b/core/java/android/os/WakeLockStats.java
new file mode 100644
index 0000000..05a7313
--- /dev/null
+++ b/core/java/android/os/WakeLockStats.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Snapshot of wake lock stats.
+ * @hide
+ */
+public final class WakeLockStats implements Parcelable {
+
+ /** @hide */
+ public static class WakeLock {
+ public final int uid;
+ @NonNull
+ public final String name;
+ public final int timesAcquired;
+ public final long totalTimeHeldMs;
+
+ /**
+ * Time in milliseconds that the lock has been held or 0 if not currently holding the lock
+ */
+ public final long timeHeldMs;
+
+ public WakeLock(int uid, @NonNull String name, int timesAcquired, long totalTimeHeldMs,
+ long timeHeldMs) {
+ this.uid = uid;
+ this.name = name;
+ this.timesAcquired = timesAcquired;
+ this.totalTimeHeldMs = totalTimeHeldMs;
+ this.timeHeldMs = timeHeldMs;
+ }
+
+ private WakeLock(Parcel in) {
+ uid = in.readInt();
+ name = in.readString();
+ timesAcquired = in.readInt();
+ totalTimeHeldMs = in.readLong();
+ timeHeldMs = in.readLong();
+ }
+
+ private void writeToParcel(Parcel out) {
+ out.writeInt(uid);
+ out.writeString(name);
+ out.writeInt(timesAcquired);
+ out.writeLong(totalTimeHeldMs);
+ out.writeLong(timeHeldMs);
+ }
+
+ @Override
+ public String toString() {
+ return "WakeLock{"
+ + "uid=" + uid
+ + ", name='" + name + '\''
+ + ", timesAcquired=" + timesAcquired
+ + ", totalTimeHeldMs=" + totalTimeHeldMs
+ + ", timeHeldMs=" + timeHeldMs
+ + '}';
+ }
+ }
+
+ private final List<WakeLock> mWakeLocks;
+
+ /** @hide **/
+ public WakeLockStats(@NonNull List<WakeLock> wakeLocks) {
+ mWakeLocks = wakeLocks;
+ }
+
+ @NonNull
+ public List<WakeLock> getWakeLocks() {
+ return mWakeLocks;
+ }
+
+ private WakeLockStats(Parcel in) {
+ final int size = in.readInt();
+ mWakeLocks = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ mWakeLocks.add(new WakeLock(in));
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ final int size = mWakeLocks.size();
+ out.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ WakeLock stats = mWakeLocks.get(i);
+ stats.writeToParcel(out);
+ }
+ }
+
+ @NonNull
+ public static final Creator<WakeLockStats> CREATOR =
+ new Creator<WakeLockStats>() {
+ public WakeLockStats createFromParcel(Parcel in) {
+ return new WakeLockStats(in);
+ }
+
+ public WakeLockStats[] newArray(int size) {
+ return new WakeLockStats[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "WakeLockStats " + mWakeLocks;
+ }
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 5e4057b..29accb9 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1525,10 +1525,11 @@
result = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100;
} else {
// Else, linearly interpolate the amount of space to reserve
- result = ((CACHE_RESERVE_PERCENT_HIGH - CACHE_RESERVE_PERCENT_LOW)
- * (usableBytes - storageThresholdHighBytes) + CACHE_RESERVE_PERCENT_HIGH
- * (storageThresholdHighBytes - storageThresholdLowBytes)) * totalBytes
- / (100 * (storageThresholdHighBytes - storageThresholdLowBytes));
+ double slope = (CACHE_RESERVE_PERCENT_HIGH - CACHE_RESERVE_PERCENT_LOW) * totalBytes
+ / (100.0 * (storageThresholdHighBytes - storageThresholdLowBytes));
+ double intercept = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100.0
+ - storageThresholdLowBytes * slope;
+ result = Math.round(slope * usableBytes + intercept);
}
return result;
}
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 66e1c5a..0e32a78 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -54,4 +54,6 @@
void getGroupOfPlatformPermission(
in String permissionName,
in AndroidFuture<String> callback);
+ void getUnusedAppCount(
+ in AndroidFuture callback);
}
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 00f9e45..a0788e7 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -69,6 +69,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
+import java.util.function.IntConsumer;
/**
* Interface for communicating with the permission controller.
@@ -786,4 +787,34 @@
}
}, executor);
}
+
+ /**
+ * Get the number of unused, hibernating apps for the user.
+ *
+ * @param executor executor to run callback on
+ * @param callback callback for when result is generated
+ */
+ public void getUnusedAppCount(@NonNull @CallbackExecutor Executor executor,
+ @NonNull IntConsumer callback) {
+ checkNotNull(executor);
+ checkNotNull(callback);
+
+ mRemoteService.postAsync(service -> {
+ AndroidFuture<Integer> unusedAppCountResult = new AndroidFuture<>();
+ service.getUnusedAppCount(unusedAppCountResult);
+ return unusedAppCountResult;
+ }).whenCompleteAsync((count, err) -> {
+ if (err != null) {
+ Log.e(TAG, "Error getting unused app count", err);
+ callback.accept(0);
+ } else {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ callback.accept((int) count);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
}
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 8854e27..c979303 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -340,6 +340,20 @@
throw new AbstractMethodError("Must be overridden in implementing class");
}
+ /**
+ * Get the count of unused, hibernating apps on the device.
+ *
+ * @param callback callback after count is retrieved
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_APP_HIBERNATION)
+ @NonNull
+ public void onGetUnusedAppCount(@NonNull IntConsumer callback) {
+ throw new AbstractMethodError("Must be overridden in implementing class");
+ }
+
@Override
public final @NonNull IBinder onBind(Intent intent) {
return new IPermissionController.Stub() {
@@ -618,6 +632,20 @@
callback.completeExceptionally(t);
}
}
+
+ @Override
+ public void getUnusedAppCount(@NonNull AndroidFuture callback) {
+ try {
+ Objects.requireNonNull(callback);
+
+ enforceSomePermissionsGrantedToCaller(
+ Manifest.permission.MANAGE_APP_HIBERNATION);
+
+ PermissionControllerService.this.onGetUnusedAppCount(callback::complete);
+ } catch (Throwable t) {
+ callback.completeExceptionally(t);
+ }
+ }
};
}
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 17f57a8..61e48c5 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -24,6 +24,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -101,6 +103,26 @@
*/
public static final int PERMISSION_HARD_DENIED = 2;
+ /**
+ * Activity action: Launch UI to review permission decisions.
+ * <p>
+ * <strong>Important:</strong>You must protect the activity that handles this action with the
+ * {@link android.Manifest.permission#START_REVIEW_PERMISSION_DECISIONS} permission to ensure
+ * that only the system can launch this activity. The system will not launch activities that are
+ * not properly protected.
+ * <p>
+ * Input: Nothing.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @RequiresPermission(android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS)
+ public static final String ACTION_REVIEW_PERMISSION_DECISIONS =
+ "android.permission.action.REVIEW_PERMISSION_DECISIONS";
+
+
/** @hide */
public static final String LOG_TAG_TRACE_GRANTS = "PermissionGrantTrace";
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index f0e6624..658e033 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -318,7 +318,7 @@
String permGroup = usedPermGroups.get(permGroupNum);
ArrayMap<OpUsage, CharSequence> usagesWithLabels =
- getUniqueUsagesWithLabels(rawUsages.get(permGroup));
+ getUniqueUsagesWithLabels(permGroup, rawUsages.get(permGroup));
if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
isPhone = true;
@@ -439,7 +439,8 @@
return ListFormatter.getInstance().format(labels);
}
- private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(List<OpUsage> usages) {
+ private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(String permGroup,
+ List<OpUsage> usages) {
ArrayMap<OpUsage, CharSequence> usagesAndLabels = new ArrayMap<>();
if (usages == null || usages.isEmpty()) {
@@ -474,7 +475,7 @@
// If this usage has a proxy, but is not a proxy, it is the end of a chain.
// TODO remove once camera converted
if (!proxies.containsKey(usageAttr) && usage.proxy != null
- && !usage.op.equals(OPSTR_RECORD_AUDIO)) {
+ && !MICROPHONE.equals(permGroup)) {
proxyLabels.put(usage, new ArrayList<>());
proxyPackages.add(usage.getPackageIdHash());
}
@@ -546,7 +547,7 @@
// TODO ntmyren: remove this proxy logic once camera is converted to AttributionSource
// For now: don't add mic proxy usages
- if (!start.op.equals(OPSTR_RECORD_AUDIO)) {
+ if (!MICROPHONE.equals(permGroup)) {
usagesAndLabels.put(start,
proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
}
@@ -560,7 +561,8 @@
// if the list is empty or incomplete, do not show it.
if (usageList.isEmpty() || !usageList.get(lastVisible).isEnd()
|| !usageList.get(0).isStart()
- || !usageList.get(lastVisible).usage.op.equals(OPSTR_RECORD_AUDIO)) {
+ || !permGroup.equals(getGroupForOp(usageList.get(0).usage.op))
+ || !MICROPHONE.equals(permGroup)) {
continue;
}
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index dd2ea81..5d00b29 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -231,7 +231,7 @@
prefix = { "STATUS_" },
value = {STATUS_NOT_BLOCKED, STATUS_BLOCKED_IN_LIST, STATUS_BLOCKED_RESTRICTED,
STATUS_BLOCKED_UNKNOWN_NUMBER, STATUS_BLOCKED_PAYPHONE,
- STATUS_BLOCKED_NOT_IN_CONTACTS})
+ STATUS_BLOCKED_NOT_IN_CONTACTS, STATUS_BLOCKED_UNAVAILABLE})
public @interface BlockStatus {}
/**
@@ -277,6 +277,13 @@
public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5;
/**
+ * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
+ * because it is from a number not available.
+ * @hide
+ */
+ public static final int STATUS_BLOCKED_UNAVAILABLE = 6;
+
+ /**
* Integer reason indicating whether a call was blocked, and if so why.
* @hide
*/
@@ -441,6 +448,9 @@
/* Preference key for whether should show an emergency call notification. */
public static final String ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION =
"show_emergency_call_notification";
+ /* Preference key of block unavailable calls setting. */
+ public static final String ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE =
+ "block_unavailable_calls_setting";
/**
* Notifies the provider that emergency services were contacted by the user.
@@ -547,6 +557,7 @@
* {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
* {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
* {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
+ * {@link #ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE}
* {@link #ENHANCED_SETTING_KEY_EMERGENCY_CALL_NOTIFICATION_SHOWING}
* @return {@code true} if the setting is enabled. {@code false} otherwise.
*/
@@ -574,6 +585,7 @@
* {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
* {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
* {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
+ * {@link #ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE}
* {@link #ENHANCED_SETTING_KEY_EMERGENCY_CALL_NOTIFICATION_SHOWING}
* @param value the enabled statue of the setting to set.
*/
@@ -603,6 +615,8 @@
return "blocked - payphone";
case STATUS_BLOCKED_NOT_IN_CONTACTS:
return "blocked - not in contacts";
+ case STATUS_BLOCKED_UNAVAILABLE:
+ return "blocked - unavailable";
}
return "unknown";
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 0cc5bfd..8dca7ab 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -910,6 +910,7 @@
* <li>{@link #PRESENTATION_RESTRICTED}</li>
* <li>{@link #PRESENTATION_UNKNOWN}</li>
* <li>{@link #PRESENTATION_PAYPHONE}</li>
+ * <li>{@link #PRESENTATION_UNAVAILABLE}</li>
* </ul>
* </p>
*
@@ -925,6 +926,8 @@
public static final int PRESENTATION_UNKNOWN = 3;
/** Number is a pay phone. */
public static final int PRESENTATION_PAYPHONE = 4;
+ /** Number is unavailable. */
+ public static final int PRESENTATION_UNAVAILABLE = 5;
/**
* The ISO 3166-1 two letters country code of the country where the
@@ -2028,6 +2031,10 @@
return presentation;
}
+ if (presentation == TelecomManager.PRESENTATION_UNAVAILABLE) {
+ return PRESENTATION_UNAVAILABLE;
+ }
+
if (TextUtils.isEmpty(number)
|| presentation == TelecomManager.PRESENTATION_UNKNOWN) {
return PRESENTATION_UNKNOWN;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 190b8f6..55ffdab 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -391,6 +391,21 @@
"android.settings.REDUCE_BRIGHT_COLORS_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of Color correction.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_COLOR_CORRECTION_SETTINGS =
+ "com.android.settings.ACCESSIBILITY_COLOR_SPACE_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of Color inversion.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -950,6 +965,19 @@
"android.settings.LOCALE_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of per application locale.
+ * <p>
+ * Input: The Intent's data URI can specify the application package name to directly invoke the
+ * app locale details GUI specific to the package name.
+ * For example "package:com.my.app".
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APP_LOCALE_SETTINGS =
+ "android.settings.APP_LOCALE_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of lockscreen.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -6524,6 +6552,16 @@
@Readable
public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
+
+ /**
+ * Setting to indicate live caption button show or hide in the volume
+ * rocker.
+ *
+ * @hide
+ */
+ public static final String ODI_CAPTIONS_VOLUME_UI_ENABLED =
+ "odi_captions_volume_ui_enabled";
+
/**
* On Android 8.0 (API level 26) and higher versions of the platform,
* a 64-bit number (expressed as a hexadecimal string), unique to
@@ -10081,6 +10119,14 @@
"theme_customization_overlay_packages";
/**
+ * Indicates whether the device is in kids nav mode.
+ * <p>Type: int (0 for false, 1 for true)
+ *
+ * @hide
+ */
+ public static final String NAV_BAR_KIDS_MODE = "nav_bar_kids_mode";
+
+ /**
* Navigation bar mode.
* 0 = 3 button
* 1 = 2 button
@@ -10444,13 +10490,6 @@
public static final String COMMUNAL_MODE_ENABLED = "communal_mode_enabled";
/**
- * An array of all the packages which have been enabled for hub mode by the user.
- *
- * @hide
- */
- public static final String COMMUNAL_MODE_PACKAGES = "communal_mode_packages";
-
- /**
* An array of SSIDs of Wi-Fi networks that, when connected, are considered safe to enable
* the communal mode.
*
@@ -15178,7 +15217,10 @@
* {@code p1[url_bar]:p2:p3[url_foo,url_bas]}
*
* @hide
+ * @deprecated Use {@link android.view.autofill.AutofillManager
+ * #DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES} instead.
*/
+ @Deprecated
@SystemApi
@Readable
public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES =
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index e3c3969..34e35d4 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3589,7 +3589,7 @@
* @hide
*/
public static final Uri PREFERRED_APN_URI = Uri.parse(
- "content://telephony/carriers/preferapn/subId/");
+ "content://telephony/carriers/preferapn/subId");
/**
* The {@code content://} style URL for the perferred APN set id.
@@ -3597,8 +3597,15 @@
* @hide
*/
public static final Uri PREFERRED_APN_SET_URI = Uri.parse(
- "content://telephony/carriers/preferapnset/subId/");
+ "content://telephony/carriers/preferapnset/subId");
+ /**
+ * The id of preferred APN.
+ *
+ * @see #PREFERRED_APN_URI
+ * @hide
+ */
+ public static final String APN_ID = "apn_id";
/**
* The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
new file mode 100644
index 0000000..8524ac84
--- /dev/null
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Initiates handwriting mode once it detects stylus movement in handwritable areas.
+ *
+ * It is designed to be used by {@link ViewRootImpl}. For every stylus related MotionEvent that is
+ * dispatched to view tree, ViewRootImpl should call {@link #onTouchEvent} method of this class.
+ * And it will automatically request to enter the handwriting mode when the conditions meet.
+ *
+ * Notice that ViewRootImpl should still dispatch MotionEvents to view tree as usual.
+ * And if it successfully enters the handwriting mode, the ongoing MotionEvent stream will be
+ * routed to the input method. Input system will fabricate an ACTION_CANCEL and send to
+ * ViewRootImpl.
+ *
+ * This class does nothing if:
+ * a) MotionEvents are not from stylus.
+ * b) The user taps or long-clicks with a stylus etc.
+ * c) Stylus pointer down position is not within a handwritable area.
+ *
+ * Used by InputMethodManager.
+ * @hide
+ */
+public class HandwritingInitiator {
+ /**
+ * The touchSlop from {@link ViewConfiguration} used to decide whether a pointer is considered
+ * moving or stationary.
+ */
+ private final int mTouchSlop;
+ /**
+ * The timeout used to distinguish tap from handwriting. If the stylus doesn't move before this
+ * timeout, it's not considered as handwriting.
+ */
+ private final long mTapTimeoutInMillis;
+
+ private State mState = new State();
+
+ /**
+ * Helper method to reset the internal state of this class.
+ * Calling this method will also prevent the following MotionEvents
+ * triggers handwriting until the next stylus ACTION_DOWN/ACTION_POINTER_DOWN
+ * arrives.
+ */
+ private void reset() {
+ mState = new State();
+ }
+
+ /** The reference to the View that currently has the input connection. */
+ @Nullable
+ @VisibleForTesting
+ public WeakReference<View> mConnectedView = null;
+
+ /** The editor bound reported by the connected View. */
+ @Nullable
+ @VisibleForTesting
+ public Rect mEditorBound = null;
+
+ /**
+ * When InputConnection restarts for a View, View#onInputConnectionCreatedInternal
+ * might be called before View#onInputConnectionClosedInternal, so we need to count the input
+ * connections and only set mConnectedView to null when mConnectionCount is zero.
+ */
+ private int mConnectionCount = 0;
+ private final InputMethodManager mImm;
+
+ @VisibleForTesting
+ public HandwritingInitiator(ViewConfiguration viewConfiguration,
+ InputMethodManager inputMethodManager) {
+ mTouchSlop = viewConfiguration.getScaledTouchSlop();
+ mTapTimeoutInMillis = ViewConfiguration.getTapTimeout();
+ mImm = inputMethodManager;
+ }
+
+ /**
+ * Notify the HandwritingInitiator that a new MotionEvent has arrived.
+ * This method is non-block, and the event passed to this method should be dispatched to the
+ * View tree as usual. If HandwritingInitiator triggers the handwriting mode, an fabricated
+ * ACTION_CANCEL event will be sent to the ViewRootImpl.
+ * @param motionEvent the stylus MotionEvent.
+ */
+ @VisibleForTesting
+ public void onTouchEvent(MotionEvent motionEvent) {
+ final int maskedAction = motionEvent.getActionMasked();
+ switch (maskedAction) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ final int actionIndex = motionEvent.getActionIndex();
+ final int toolType = motionEvent.getToolType(actionIndex);
+ // TOOL_TYPE_ERASER is also from stylus. This indicates that the user is holding
+ // the eraser button during handwriting.
+ if (toolType != MotionEvent.TOOL_TYPE_STYLUS
+ && toolType != MotionEvent.TOOL_TYPE_ERASER) {
+ // The motion event is not from a stylus event, ignore it.
+ return;
+ }
+ mState.mStylusPointerId = motionEvent.getPointerId(actionIndex);
+ mState.mStylusDownTimeInMillis = motionEvent.getEventTime();
+ mState.mStylusDownX = motionEvent.getX(actionIndex);
+ mState.mStylusDownY = motionEvent.getY(actionIndex);
+ mState.mShouldInitHandwriting = true;
+ mState.mExceedTouchSlop = false;
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ final int pointerId = motionEvent.getPointerId(motionEvent.getActionIndex());
+ if (pointerId != mState.mStylusPointerId) {
+ // ACTION_POINTER_UP is from another stylus pointer, ignore the event.
+ return;
+ }
+ // Deliberately fall through.
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // If it's ACTION_CANCEL or ACTION_UP, all the pointers go up. There is no need to
+ // check whether the stylus we are tracking goes up.
+ reset();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // Either we've already tried to initiate handwriting, or the ongoing MotionEvent
+ // sequence is considered to be tap, long-click or other gestures.
+ if (!mState.mShouldInitHandwriting || mState.mExceedTouchSlop) {
+ return;
+ }
+
+ final long timeElapsed =
+ motionEvent.getEventTime() - mState.mStylusDownTimeInMillis;
+ if (timeElapsed > mTapTimeoutInMillis) {
+ reset();
+ return;
+ }
+
+ final int pointerIndex = motionEvent.findPointerIndex(mState.mStylusPointerId);
+ final float x = motionEvent.getX(pointerIndex);
+ final float y = motionEvent.getY(pointerIndex);
+ if (largerThanTouchSlop(x, y, mState.mStylusDownX, mState.mStylusDownY)) {
+ mState.mExceedTouchSlop = true;
+ tryStartHandwriting();
+ }
+ }
+ }
+
+ private View getConnectedView() {
+ if (mConnectedView == null) return null;
+ return mConnectedView.get();
+ }
+
+ /**
+ * Notify HandwritingInitiator that a new InputConnection is created.
+ * The caller of this method should guarantee that each onInputConnectionCreated call
+ * is paired with a onInputConnectionClosed call.
+ * @param view the view that created the current InputConnection.
+ * @see #onInputConnectionClosed(View)
+ */
+ public void onInputConnectionCreated(@NonNull View view, @NonNull EditorInfo editorInfo) {
+ final View connectedView = getConnectedView();
+// updateEditorBound(editorInfo.getInitialEditorBound());
+ if (connectedView == view) {
+ ++mConnectionCount;
+ } else {
+ mConnectedView = new WeakReference<>(view);
+ mConnectionCount = 1;
+ tryStartHandwriting();
+ }
+ }
+
+ /**
+ * Notify HandwritingInitiator that the InputConnection has closed for the given view.
+ * The caller of this method should guarantee that each onInputConnectionClosed call
+ * is paired with a onInputConnectionCreated call.
+ * @param view the view that closed the InputConnection.
+ */
+ public void onInputConnectionClosed(@NonNull View view) {
+ final View connectedView = getConnectedView();
+ if (connectedView == view) {
+ --mConnectionCount;
+ if (mConnectionCount == 0) {
+ mConnectedView = null;
+ mEditorBound = null;
+ }
+ } else {
+ // Unexpected branch, set mConnectedView to null to avoid further problem.
+ mConnectedView = null;
+ mEditorBound = null;
+ mConnectionCount = 0;
+ }
+ }
+
+ /**
+ * Notify the HandwritingInitiator that editor bound of the connected view(the view with
+ * active InputConnection) has be updated.
+ * @param editorBound new the editor bounds of the connected view.
+ */
+ public void updateEditorBound(@NonNull Rect editorBound) {
+ if (mEditorBound == null) {
+ mEditorBound = new Rect(editorBound);
+ } else {
+ mEditorBound.left = editorBound.left;
+ mEditorBound.top = editorBound.top;
+ mEditorBound.right = editorBound.right;
+ mEditorBound.bottom = editorBound.bottom;
+ }
+ }
+
+ /**
+ * Try to initiate handwriting. For this method to successfully send startHandwriting signal,
+ * the following 3 conditions should meet:
+ * a) The stylus movement exceeds the touchSlop.
+ * b) A View has built InputConnection with IME.
+ * c) The stylus event lands into the connected View's boundary.
+ * This method will immediately fail without any side effect if condition a or b is not met.
+ * However, if both condition a and b are met but the condition c is not met, it will reset the
+ * internal states. And HandwritingInitiator won't attempt to call startHandwriting until the
+ * next ACTION_DOWN.
+ */
+ private void tryStartHandwriting() {
+ if (!mState.mExceedTouchSlop) {
+ return;
+ }
+ final View connectedView = getConnectedView();
+ if (connectedView == null || mEditorBound == null) {
+ return;
+ }
+ final ViewParent viewParent = connectedView.getParent();
+ // Do a final check before startHandwriting.
+ if (viewParent != null && connectedView.isAttachedToWindow()) {
+ final Rect editorBounds = new Rect(mEditorBound);
+ if (viewParent.getChildVisibleRect(connectedView, editorBounds, null)) {
+ final int roundedInitX = Math.round(mState.mStylusDownX);
+ final int roundedInitY = Math.round(mState.mStylusDownY);
+ if (editorBounds.contains(roundedInitX, roundedInitY)) {
+ startHandwriting(mConnectedView.get());
+ }
+ }
+ }
+ reset();
+ }
+
+ /** For test only. */
+ @VisibleForTesting
+ public void startHandwriting(View view) {
+ // mImm.startHandwriting(view);
+ }
+
+ private boolean largerThanTouchSlop(float x1, float y1, float x2, float y2) {
+ float dx = x1 - x2;
+ float dy = y1 - y2;
+ return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
+ }
+
+ /** Object that keeps the MotionEvent related states for HandwritingInitiator. */
+ private static class State {
+ /**
+ * Whether it should initiate handwriting mode for the current MotionEvent sequence.
+ * (A series of MotionEvents from ACTION_DOWN to ACTION_UP)
+ *
+ * The purpose of this boolean value is:
+ * a) We should only request to start handwriting mode ONCE for each MotionEvent sequence.
+ * If we've already requested to enter handwriting mode for the ongoing MotionEvent
+ * sequence, this boolean is set to false. And it won't request to start handwriting again.
+ *
+ * b) If the MotionEvent sequence is considered to be tap, long-click or other gestures.
+ * This boolean will be set to false, and it won't request to start handwriting.
+ */
+ private boolean mShouldInitHandwriting = false;
+ /**
+ * Whether the current ongoing stylus MotionEvent sequence already exceeds the touchSlop.
+ * It's used for the case where the stylus exceeds touchSlop before the target View built
+ * InputConnection.
+ */
+ private boolean mExceedTouchSlop = false;
+
+ /** The pointer id of the stylus pointer that is being tracked. */
+ private int mStylusPointerId = -1;
+ /** The time stamp when the stylus pointer goes down. */
+ private long mStylusDownTimeInMillis = -1;
+ /** The initial location where the stylus pointer goes down. */
+ private float mStylusDownX = Float.NaN;
+ private float mStylusDownY = Float.NaN;
+ }
+}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 8db62f6..adb8b86 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1872,7 +1872,7 @@
float x, float y, float pressure, float size, int metaState,
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
return obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
- xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_CLASS_POINTER,
+ xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_UNKNOWN,
DEFAULT_DISPLAY);
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d0a5835..49ece5f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -972,9 +972,9 @@
mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId();
if (mViewVisibility) {
- mTmpTransaction.show(mSurfaceControl);
+ geometryTransaction.show(mSurfaceControl);
} else {
- mTmpTransaction.hide(mSurfaceControl);
+ geometryTransaction.hide(mSurfaceControl);
}
if (mSurfacePackage != null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8fda48b..258359e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15272,7 +15272,7 @@
* @param event the KeyEvent object that defines the button action
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (KeyEvent.isConfirmKey(keyCode)) {
+ if (event.hasNoModifiers() && KeyEvent.isConfirmKey(keyCode)) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
@@ -15329,7 +15329,7 @@
* @param event The KeyEvent object that defines the button action.
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (KeyEvent.isConfirmKey(keyCode)) {
+ if (event.hasNoModifiers() && KeyEvent.isConfirmKey(keyCode)) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 748e551..7718511 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -752,6 +752,17 @@
int localChanges;
}
+ private final HandwritingInitiator mHandwritingInitiator;
+
+ /**
+ * Used by InputMethodManager.
+ * @hide
+ */
+ @NonNull
+ public HandwritingInitiator getHandwritingInitiator() {
+ return mHandwritingInitiator;
+ }
+
/**
* This is only used on the RenderThread when handling a blast sync. Specifically, it's only
* used when calling {@link BLASTBufferQueue#setSyncTransaction(Transaction)} and then merged
@@ -826,6 +837,8 @@
? Choreographer.getSfInstance() : Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
+ mHandwritingInitiator = new HandwritingInitiator(mViewConfiguration,
+ mContext.getSystemService(InputMethodManager.class));
String processorOverrideName = context.getResources().getString(
R.string.config_inputEventCompatProcessorOverrideClassName);
@@ -6418,6 +6431,7 @@
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
+ mHandwritingInitiator.onTouchEvent(event);
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
@@ -7596,7 +7610,7 @@
// When a new focused view is selected, we consume the navigation key because
// navigation doesn't make much sense unless a view already has focus so
// the key's purpose is to set focus.
- if (isNavigationKey(event)) {
+ if (event.hasNoModifiers() && isNavigationKey(event)) {
return ensureTouchMode(false);
}
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index 91ef8a5..e6385a5 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -46,6 +46,8 @@
private static final boolean CHECK_INTEGRITY = Build.IS_ENG;
+ private boolean mEnabled = true;
+
/**
* {@link AccessibilityEvent} types that are critical for the cache to stay up to date
*
@@ -98,6 +100,21 @@
mAccessibilityNodeRefresher = nodeRefresher;
}
+ /** Returns if the cache is enabled. */
+ public boolean isEnabled() {
+ synchronized (mLock) {
+ return mEnabled;
+ }
+ }
+
+ /** Sets enabled status. */
+ public void setEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mEnabled = enabled;
+ clear();
+ }
+ }
+
/**
* Sets all {@link AccessibilityWindowInfo}s of all displays into the cache.
* The key of SparseArray is display ID.
@@ -110,6 +127,12 @@
SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays,
long populationTimeStamp) {
synchronized (mLock) {
+ if (!mEnabled) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache is disabled");
+ }
+ return;
+ }
if (DEBUG) {
Log.i(LOG_TAG, "Set windows");
}
@@ -148,6 +171,12 @@
*/
public void addWindow(AccessibilityWindowInfo window) {
synchronized (mLock) {
+ if (!mEnabled) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache is disabled");
+ }
+ return;
+ }
if (DEBUG) {
Log.i(LOG_TAG, "Caching window: " + window.getId() + " at display Id [ "
+ window.getDisplayId() + " ]");
@@ -177,6 +206,12 @@
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo nodeToRefresh = null;
synchronized (mLock) {
+ if (!mEnabled) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache is disabled");
+ }
+ return;
+ }
if (DEBUG) {
Log.i(LOG_TAG, "onAccessibilityEvent(" + event + ")");
}
@@ -292,6 +327,12 @@
*/
public AccessibilityNodeInfo getNode(int windowId, long accessibilityNodeId) {
synchronized(mLock) {
+ if (!mEnabled) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache is disabled");
+ }
+ return null;
+ }
LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
if (nodes == null) {
return null;
@@ -309,6 +350,28 @@
}
}
+ /** Returns {@code true} if {@code info} is in the cache. */
+ public boolean isNodeInCache(AccessibilityNodeInfo info) {
+ if (info == null) {
+ return false;
+ }
+ int windowId = info.getWindowId();
+ long accessibilityNodeId = info.getSourceNodeId();
+ synchronized (mLock) {
+ if (!mEnabled) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache is disabled");
+ }
+ return false;
+ }
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes == null) {
+ return false;
+ }
+ return nodes.get(accessibilityNodeId) != null;
+ }
+ }
+
/**
* Gets all {@link AccessibilityWindowInfo}s of all displays from the cache.
*
@@ -317,6 +380,12 @@
*/
public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
synchronized (mLock) {
+ if (!mEnabled) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache is disabled");
+ }
+ return null;
+ }
if (!mIsAllWindowsCached) {
return null;
}
@@ -373,6 +442,12 @@
*/
public AccessibilityWindowInfo getWindow(int windowId) {
synchronized (mLock) {
+ if (!mEnabled) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache is disabled");
+ }
+ return null;
+ }
final int displayCounts = mWindowCacheByDisplay.size();
for (int i = 0; i < displayCounts; i++) {
final SparseArray<AccessibilityWindowInfo> windowsOfDisplay =
@@ -397,6 +472,12 @@
*/
public void add(AccessibilityNodeInfo info) {
synchronized(mLock) {
+ if (!mEnabled) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache is disabled");
+ }
+ return;
+ }
if (VERBOSE) {
Log.i(LOG_TAG, "add(" + info + ")");
}
@@ -522,6 +603,12 @@
*/
public AccessibilityNodeInfo getFocus(int focusType, long initialNodeId, int windowId) {
synchronized (mLock) {
+ if (!mEnabled) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache is disabled");
+ }
+ return null;
+ }
int currentFocusWindowId;
long currentFocusId;
if (focusType == FOCUS_ACCESSIBILITY) {
@@ -602,6 +689,23 @@
mNodeCache.remove(windowId);
}
+ /** Clears a subtree rooted at {@code info}. */
+ public boolean clearSubTree(AccessibilityNodeInfo info) {
+ if (info == null) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (!mEnabled) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache is disabled");
+ }
+ return false;
+ }
+ clearSubTreeLocked(info.getWindowId(), info.getSourceNodeId());
+ return true;
+ }
+ }
+
/**
* Clears a subtree rooted at the node with the given id that is
* hosted in a given window.
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 83712b4..a427ab8 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -24,7 +24,6 @@
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Pools.SynchronizedPool;
import com.android.internal.util.BitUtils;
@@ -806,10 +805,6 @@
*/
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
- private static final int MAX_POOL_SIZE = 10;
- private static final SynchronizedPool<AccessibilityEvent> sPool =
- new SynchronizedPool<>(MAX_POOL_SIZE);
-
@UnsupportedAppUsage
private @EventType int mEventType;
private CharSequence mPackageName;
@@ -1170,7 +1165,7 @@
*/
public static AccessibilityEvent obtainWindowsChangedEvent(
int windowId, int windowChangeTypes) {
- final AccessibilityEvent event = AccessibilityEvent.obtain(TYPE_WINDOWS_CHANGED);
+ final AccessibilityEvent event = new AccessibilityEvent(TYPE_WINDOWS_CHANGED);
event.setWindowId(windowId);
event.setWindowChanges(windowChangeTypes);
event.setImportantForAccessibility(true);
@@ -1178,69 +1173,58 @@
}
/**
- * Returns a cached instance if such is available or a new one is
- * instantiated with its type property set.
+ * Instantiates a new AccessibilityEvent instance with its type property set.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
- * constructor {@link #AccessibilityEvent(int)} instead.
- *
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link #AccessibilityEvent()} instead.
* @param eventType The event type.
* @return An instance.
*/
+ @Deprecated
public static AccessibilityEvent obtain(int eventType) {
- AccessibilityEvent event = AccessibilityEvent.obtain();
+ AccessibilityEvent event = new AccessibilityEvent();
event.setEventType(eventType);
return event;
}
/**
- * Returns a cached instance if such is available or a new one is
- * created. The returned instance is initialized from the given
+ * Instantiates a new AccessibilityEvent instance.
+ * The returned instance is initialized from the given
* <code>event</code>.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
- * constructor {@link #AccessibilityEvent(AccessibilityEvent)} instead.
- *
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link #AccessibilityEvent()} instead.
* @param event The other event.
* @return An instance.
*/
+ @Deprecated
public static AccessibilityEvent obtain(AccessibilityEvent event) {
- AccessibilityEvent eventClone = AccessibilityEvent.obtain();
+ AccessibilityEvent eventClone = new AccessibilityEvent();
eventClone.init(event);
return eventClone;
}
/**
- * Returns a cached instance if such is available or a new one is
- * instantiated.
+ * Instantiates a new AccessibilityEvent instance.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityEvent()} instead.
- *
* @return An instance.
*/
+ @Deprecated
public static AccessibilityEvent obtain() {
- AccessibilityEvent event = sPool.acquire();
- if (event == null) event = new AccessibilityEvent();
- if (DEBUG_ORIGIN) event.originStackTrace = Thread.currentThread().getStackTrace();
- return event;
+ return new AccessibilityEvent();
}
/**
- * Recycles an instance back to be reused.
- * <p>
- * <b>Note: You must not touch the object after calling this function.</b>
- * </p>
+ * Previously would recycle an instance back to be reused.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
- *
- * @throws IllegalStateException If the event is already recycled.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
@Override
- public void recycle() {
- clear();
- sPool.release(this);
- }
+ @Deprecated
+ public void recycle() {}
/**
* Clears the state of this instance.
@@ -1260,7 +1244,6 @@
if (mRecords != null) {
while (!mRecords.isEmpty()) {
AccessibilityRecord record = mRecords.remove(0);
- record.recycle();
}
}
if (DEBUG_ORIGIN) originStackTrace = null;
@@ -1288,7 +1271,7 @@
if (recordCount > 0) {
mRecords = new ArrayList<>(recordCount);
for (int i = 0; i < recordCount; i++) {
- AccessibilityRecord record = AccessibilityRecord.obtain();
+ AccessibilityRecord record = new AccessibilityRecord();
readAccessibilityRecordFromParcel(record, parcel);
record.mConnectionId = mConnectionId;
mRecords.add(record);
@@ -1527,7 +1510,7 @@
public static final @android.annotation.NonNull Parcelable.Creator<AccessibilityEvent> CREATOR =
new Parcelable.Creator<AccessibilityEvent>() {
public AccessibilityEvent createFromParcel(Parcel parcel) {
- AccessibilityEvent event = AccessibilityEvent.obtain();
+ AccessibilityEvent event = new AccessibilityEvent();
event.initFromParcel(parcel);
return event;
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index dc4c59a..6f4bc71 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -18,6 +18,7 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+import static android.os.Build.VERSION_CODES.S;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
@@ -225,6 +226,9 @@
*/
public static void addConnection(int connectionId, IAccessibilityServiceConnection connection,
boolean initializeCache) {
+ if (connectionId == NO_ID) {
+ return;
+ }
synchronized (sConnectionCache) {
sConnectionCache.put(connectionId, connection);
if (!initializeCache) {
@@ -554,6 +558,10 @@
}
return cachedInfo;
}
+ if (!cache.isEnabled()) {
+ // Skip prefetching if cache is disabled.
+ prefetchFlags &= ~AccessibilityNodeInfo.FLAG_PREFETCH_MASK;
+ }
if (DEBUG) {
Log.i(LOG_TAG, "Node cache miss for "
+ idToString(accessibilityWindowId, accessibilityNodeId));
@@ -970,9 +978,9 @@
/**
* Clears the cache associated with {@code connectionId}
* @param connectionId the connection id
- * TODO(207417185): Modify UnsupportedAppUsage
*/
- @UnsupportedAppUsage()
+ @UnsupportedAppUsage(maxTargetSdk = S, publicAlternatives =
+ "{@link android.accessibilityservice.AccessibilityService#clearCache()}")
public void clearCache(int connectionId) {
AccessibilityCache cache = getCache(connectionId);
if (cache == null) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 9511958..db7c663 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -50,7 +50,6 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.LongArray;
-import android.util.Pools.SynchronizedPool;
import android.util.Size;
import android.util.TypedValue;
import android.view.SurfaceView;
@@ -68,7 +67,6 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* This class represents a node of the window content as well as actions that
@@ -723,9 +721,6 @@
*/
private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
- // TODO(b/129300068): Remove sNumInstancesInUse.
- private static AtomicInteger sNumInstancesInUse;
-
/**
* Gets the accessibility view id which identifies a View in the view three.
*
@@ -769,11 +764,6 @@
return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
}
- // Housekeeping.
- private static final int MAX_POOL_SIZE = 50;
- private static final SynchronizedPool<AccessibilityNodeInfo> sPool =
- new SynchronizedPool<>(MAX_POOL_SIZE);
-
private static final AccessibilityNodeInfo DEFAULT = new AccessibilityNodeInfo();
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -869,7 +859,7 @@
* @param info The other info.
*/
public AccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info) {
- init(info, false /* usePoolingInfo */);
+ init(info);
}
/**
@@ -1009,13 +999,7 @@
if (refreshedInfo == null) {
return false;
}
- // Hard-to-reproduce bugs seem to be due to some tools recycling a node on another
- // thread. If that happens, the init will re-seal the node, which then is in a bad state
- // when it is obtained. Enforce sealing again before we init to fail when a node has been
- // recycled during a refresh to catch such errors earlier.
- enforceSealed();
- init(refreshedInfo, true /* usePoolingInfo */);
- refreshedInfo.recycle();
+ init(refreshedInfo);
return true;
}
@@ -3599,25 +3583,23 @@
* Returns a cached instance if such is available otherwise a new one
* and sets the source.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityNodeInfo(View)} instead.
- *
* @param source The source view.
* @return An instance.
*
* @see #setSource(View)
*/
+ @Deprecated
public static AccessibilityNodeInfo obtain(View source) {
- AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
- info.setSource(source);
- return info;
+ return new AccessibilityNodeInfo(source);
}
/**
* Returns a cached instance if such is available otherwise a new one
* and sets the source.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityNodeInfo(View, int)} instead.
*
* @param root The root of the virtual subtree.
@@ -3626,71 +3608,45 @@
*
* @see #setSource(View, int)
*/
+ @Deprecated
public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
- AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
- info.setSource(root, virtualDescendantId);
- return info;
+ return new AccessibilityNodeInfo(root, virtualDescendantId);
}
/**
- * Returns a cached instance if such is available otherwise a new one.
+ * Instantiates a new AccessibilityNodeInfo.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityNodeInfo()} instead.
- *
* @return An instance.
*/
+ @Deprecated
public static AccessibilityNodeInfo obtain() {
- AccessibilityNodeInfo info = sPool.acquire();
- if (sNumInstancesInUse != null) {
- sNumInstancesInUse.incrementAndGet();
- }
- return (info != null) ? info : new AccessibilityNodeInfo();
+ return new AccessibilityNodeInfo();
}
/**
- * Returns a cached instance if such is available or a new one is
- * create. The returned instance is initialized from the given
+ * Instantiates a new AccessibilityNodeInfo initialized from the given
* <code>info</code>.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityNodeInfo(AccessibilityNodeInfo)} instead.
- *
* @param info The other info.
* @return An instance.
*/
+ @Deprecated
public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
- AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
- infoClone.init(info, true /* usePoolingInfo */);
- return infoClone;
+ return new AccessibilityNodeInfo(info);
}
/**
- * Return an instance back to be reused.
- * <p>
- * <strong>Note:</strong> You must not touch the object after calling this function.
+ * Would previously return an instance back to be reused.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
- *
- * @throws IllegalStateException If the info is already recycled.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- public void recycle() {
- clear();
- sPool.release(this);
- if (sNumInstancesInUse != null) {
- sNumInstancesInUse.decrementAndGet();
- }
- }
-
- /**
- * Specify a counter that will be incremented on obtain() and decremented on recycle()
- *
- * @hide
- */
- @TestApi
- public static void setNumInstancesInUseCounter(AtomicInteger counter) {
- sNumInstancesInUse = counter;
- }
+ @Deprecated
+ public void recycle() {}
/**
* {@inheritDoc}
@@ -3704,7 +3660,6 @@
writeToParcelNoRecycle(parcel, flags);
// Since instances of this class are fetched via synchronous i.e. blocking
// calls in IPCs we always recycle as soon as the instance is marshaled.
- recycle();
}
/** @hide */
@@ -4000,9 +3955,8 @@
* Initializes this instance from another one.
*
* @param other The other instance.
- * @param usePoolingInfos whether using pooled object internally or not
*/
- private void init(AccessibilityNodeInfo other, boolean usePoolingInfos) {
+ private void init(AccessibilityNodeInfo other) {
mSealed = other.mSealed;
mSourceNodeId = other.mSourceNodeId;
mParentNodeId = other.mParentNodeId;
@@ -4062,11 +4016,7 @@
mExtras = other.mExtras != null ? new Bundle(other.mExtras) : null;
- if (usePoolingInfos) {
- initPoolingInfos(other);
- } else {
- initCopyInfos(other);
- }
+ initCopyInfos(other);
final TouchDelegateInfo otherInfo = other.mTouchDelegateInfo;
mTouchDelegateInfo = (otherInfo != null)
@@ -4077,21 +4027,6 @@
mLeashedParentNodeId = other.mLeashedParentNodeId;
}
- private void initPoolingInfos(AccessibilityNodeInfo other) {
- if (mRangeInfo != null) mRangeInfo.recycle();
- mRangeInfo = (other.mRangeInfo != null)
- ? RangeInfo.obtain(other.mRangeInfo) : null;
- if (mCollectionInfo != null) mCollectionInfo.recycle();
- mCollectionInfo = (other.mCollectionInfo != null)
- ? CollectionInfo.obtain(other.mCollectionInfo) : null;
- if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
- mCollectionItemInfo = (other.mCollectionItemInfo != null)
- ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null;
- if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
- mExtraRenderingInfo = (other.mExtraRenderingInfo != null)
- ? ExtraRenderingInfo.obtain(other.mExtraRenderingInfo) : null;
- }
-
private void initCopyInfos(AccessibilityNodeInfo other) {
RangeInfo ri = other.mRangeInfo;
mRangeInfo = (ri == null) ? null
@@ -4205,27 +4140,24 @@
? parcel.readBundle()
: null;
- if (mRangeInfo != null) mRangeInfo.recycle();
mRangeInfo = isBitSet(nonDefaultFields, fieldIndex++)
- ? RangeInfo.obtain(
+ ? new RangeInfo(
parcel.readInt(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readFloat())
: null;
- if (mCollectionInfo != null) mCollectionInfo.recycle();
mCollectionInfo = isBitSet(nonDefaultFields, fieldIndex++)
- ? CollectionInfo.obtain(
+ ? new CollectionInfo(
parcel.readInt(),
parcel.readInt(),
parcel.readInt() == 1,
parcel.readInt())
: null;
- if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
mCollectionItemInfo = isBitSet(nonDefaultFields, fieldIndex++)
- ? CollectionItemInfo.obtain(
+ ? new CollectionItemInfo(
parcel.readString(),
parcel.readInt(),
parcel.readInt(),
@@ -4241,8 +4173,7 @@
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
- if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
- mExtraRenderingInfo = ExtraRenderingInfo.obtain();
+ mExtraRenderingInfo = new ExtraRenderingInfo(null);
mExtraRenderingInfo.mLayoutSize = (Size) parcel.readValue(null);
mExtraRenderingInfo.mTextSizeInPx = parcel.readFloat();
mExtraRenderingInfo.mTextSizeUnit = parcel.readInt();
@@ -4265,7 +4196,7 @@
* Clears the state of this instance.
*/
private void clear() {
- init(DEFAULT, true /* usePoolingInfo */);
+ init(DEFAULT);
}
private static boolean isDefaultStandardAction(AccessibilityAction action) {
@@ -5235,7 +5166,6 @@
* handled by the {@link AccessibilityNodeInfo} to which this object is attached.
*/
public static final class RangeInfo {
- private static final int MAX_POOL_SIZE = 10;
/** Range type: integer. */
public static final int RANGE_TYPE_INT = 0;
@@ -5244,35 +5174,16 @@
/** Range type: percent with values from zero to one hundred. */
public static final int RANGE_TYPE_PERCENT = 2;
- private static final SynchronizedPool<RangeInfo> sPool =
- new SynchronizedPool<AccessibilityNodeInfo.RangeInfo>(MAX_POOL_SIZE);
-
private int mType;
private float mMin;
private float mMax;
private float mCurrent;
-
/**
- * Obtains a pooled instance that is a clone of another one.
+ * Instantiates a new RangeInfo.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
- * constructor {@link AccessibilityNodeInfo.RangeInfo#RangeInfo(int,
- * float, float, float)} instead.
- *
- * @param other The instance to clone.
- *
- * @hide
- */
- public static RangeInfo obtain(RangeInfo other) {
- return obtain(other.mType, other.mMin, other.mMax, other.mCurrent);
- }
-
- /**
- * Obtains a pooled instance.
- *
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
- * constructor {@link AccessibilityNodeInfo.RangeInfo#RangeInfo(int,
- * float, float, float)} instead.
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link AccessibilityNodeInfo.RangeInfo#RangeInfo(int, float, float,
+ * float)} instead.
*
* @param type The type of the range.
* @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no
@@ -5281,17 +5192,9 @@
* maximum.
* @param current The current value.
*/
+ @Deprecated
public static RangeInfo obtain(int type, float min, float max, float current) {
- RangeInfo info = sPool.acquire();
- if (info == null) {
- return new RangeInfo(type, min, max, current);
- }
-
- info.mType = type;
- info.mMin = min;
- info.mMax = max;
- info.mCurrent = current;
- return info;
+ return new RangeInfo(type, min, max, current);
}
/**
@@ -5354,12 +5257,11 @@
/**
* Recycles this instance.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- void recycle() {
- clear();
- sPool.release(this);
- }
+ @Deprecated
+ void recycle() {}
private void clear() {
mType = 0;
@@ -5392,20 +5294,15 @@
/** Selection mode where multiple items may be selected. */
public static final int SELECTION_MODE_MULTIPLE = 2;
- private static final int MAX_POOL_SIZE = 20;
-
- private static final SynchronizedPool<CollectionInfo> sPool =
- new SynchronizedPool<>(MAX_POOL_SIZE);
-
private int mRowCount;
private int mColumnCount;
private boolean mHierarchical;
private int mSelectionMode;
/**
- * Obtains a pooled instance that is a clone of another one.
+ * Instantiates a CollectionInfo that is a clone of another one.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionInfo#CollectionInfo} instead.
*
@@ -5413,14 +5310,14 @@
* @hide
*/
public static CollectionInfo obtain(CollectionInfo other) {
- return CollectionInfo.obtain(other.mRowCount, other.mColumnCount, other.mHierarchical,
+ return new CollectionInfo(other.mRowCount, other.mColumnCount, other.mHierarchical,
other.mSelectionMode);
}
/**
* Obtains a pooled instance.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionInfo#CollectionInfo(int, int,
* boolean)} instead.
@@ -5431,13 +5328,13 @@
*/
public static CollectionInfo obtain(int rowCount, int columnCount,
boolean hierarchical) {
- return obtain(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE);
+ return new CollectionInfo(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE);
}
/**
* Obtains a pooled instance.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionInfo#CollectionInfo(int, int,
* boolean, int)} instead.
@@ -5454,16 +5351,7 @@
*/
public static CollectionInfo obtain(int rowCount, int columnCount,
boolean hierarchical, int selectionMode) {
- final CollectionInfo info = sPool.acquire();
- if (info == null) {
- return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode);
- }
-
- info.mRowCount = rowCount;
- info.mColumnCount = columnCount;
- info.mHierarchical = hierarchical;
- info.mSelectionMode = selectionMode;
- return info;
+ return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode);
}
/**
@@ -5535,14 +5423,13 @@
}
/**
- * Recycles this instance.
+ * Previously would recycle this instance.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- void recycle() {
- clear();
- sPool.release(this);
- }
+ @Deprecated
+ void recycle() {}
private void clear() {
mRowCount = 0;
@@ -5566,15 +5453,10 @@
* </p>
*/
public static final class CollectionItemInfo {
- private static final int MAX_POOL_SIZE = 20;
-
- private static final SynchronizedPool<CollectionItemInfo> sPool =
- new SynchronizedPool<>(MAX_POOL_SIZE);
-
/**
- * Obtains a pooled instance that is a clone of another one.
+ * Instantiates a CollectionItemInfo that is a clone of another one.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo}
* instead.
@@ -5582,20 +5464,20 @@
* @param other The instance to clone.
* @hide
*/
+ @Deprecated
public static CollectionItemInfo obtain(CollectionItemInfo other) {
- return CollectionItemInfo.obtain(other.mRowTitle, other.mRowIndex, other.mRowSpan,
- other.mColumnTitle, other.mColumnIndex, other.mColumnSpan, other.mHeading,
- other.mSelected);
+ return new CollectionItemInfo(other.mRowTitle, other.mRowIndex, other.mRowSpan,
+ other.mColumnTitle, other.mColumnIndex, other.mColumnSpan, other.mHeading,
+ other.mSelected);
}
/**
- * Obtains a pooled instance.
+ * Instantiates a new CollectionItemInfo.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
* int, int, int, boolean)} instead.
- *
* @param rowIndex The row index at which the item is located.
* @param rowSpan The number of rows the item spans.
* @param columnIndex The column index at which the item is located.
@@ -5603,37 +5485,39 @@
* @param heading Whether the item is a heading. (Prefer
* {@link AccessibilityNodeInfo#setHeading(boolean)}).
*/
+ @Deprecated
public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
int columnIndex, int columnSpan, boolean heading) {
- return obtain(rowIndex, rowSpan, columnIndex, columnSpan, heading, false);
+ return new CollectionItemInfo(rowIndex, rowSpan, columnIndex, columnSpan, heading,
+ false);
}
/**
- * Obtains a pooled instance.
+ * Instantiates a new CollectionItemInfo.
*
- * <p>In most situations object pooling is not beneficial. Creates a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
- * int, int, int, boolean, boolean)} instead.
- *
+ * int, int, int, boolean)} instead.
* @param rowIndex The row index at which the item is located.
* @param rowSpan The number of rows the item spans.
* @param columnIndex The column index at which the item is located.
* @param columnSpan The number of columns the item spans.
* @param heading Whether the item is a heading. (Prefer
- * {@link AccessibilityNodeInfo#setHeading(boolean)})
+ * {@link AccessibilityNodeInfo#setHeading(boolean)}).
* @param selected Whether the item is selected.
*/
+ @Deprecated
public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
int columnIndex, int columnSpan, boolean heading, boolean selected) {
- return obtain(null, rowIndex, rowSpan, null, columnIndex,
- columnSpan, heading, selected);
+ return new CollectionItemInfo(rowIndex, rowSpan, columnIndex, columnSpan, heading,
+ selected);
}
/**
- * Obtains a pooled instance.
+ * Instantiates a new CollectionItemInfo.
*
- * <p>In most situations object pooling is not beneficial. Creates a new instance using the
+ * @deprecated Object pooling has been discontinued. Creates a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
* int, int, int, boolean, boolean)} instead.
@@ -5648,25 +5532,13 @@
* {@link AccessibilityNodeInfo#setHeading(boolean)})
* @param selected Whether the item is selected.
*/
+ @Deprecated
@NonNull
public static CollectionItemInfo obtain(@Nullable String rowTitle, int rowIndex,
int rowSpan, @Nullable String columnTitle, int columnIndex, int columnSpan,
boolean heading, boolean selected) {
- final CollectionItemInfo info = sPool.acquire();
- if (info == null) {
- return new CollectionItemInfo(rowTitle, rowIndex, rowSpan, columnTitle,
- columnIndex, columnSpan, heading, selected);
- }
-
- info.mRowIndex = rowIndex;
- info.mRowSpan = rowSpan;
- info.mColumnIndex = columnIndex;
- info.mColumnSpan = columnSpan;
- info.mHeading = heading;
- info.mSelected = selected;
- info.mRowTitle = rowTitle;
- info.mColumnTitle = columnTitle;
- return info;
+ return new CollectionItemInfo(rowTitle, rowIndex, rowSpan, columnTitle, columnIndex,
+ columnSpan, heading, selected);
}
private boolean mHeading;
@@ -5817,12 +5689,11 @@
/**
* Recycles this instance.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- void recycle() {
- clear();
- sPool.release(this);
- }
+ @Deprecated
+ void recycle() {}
private void clear() {
mColumnIndex = 0;
@@ -6151,34 +6022,34 @@
*/
public static final class ExtraRenderingInfo {
private static final int UNDEFINED_VALUE = -1;
- private static final int MAX_POOL_SIZE = 20;
- private static final SynchronizedPool<ExtraRenderingInfo> sPool =
- new SynchronizedPool<>(MAX_POOL_SIZE);
private Size mLayoutSize;
private float mTextSizeInPx = UNDEFINED_VALUE;
private int mTextSizeUnit = UNDEFINED_VALUE;
/**
- * Obtains a pooled instance.
+ * Instantiates an ExtraRenderingInfo, by copying an existing one.
+ *
* @hide
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link #ExtraRenderingInfo(ExtraRenderingInfo)} instead.
*/
+ @Deprecated
@NonNull
public static ExtraRenderingInfo obtain() {
- final ExtraRenderingInfo info = sPool.acquire();
- if (info == null) {
- return new ExtraRenderingInfo(null);
- }
- return info;
+ return new ExtraRenderingInfo(null);
}
- /** Obtains a pooled instance that is a clone of another one. */
+ /**
+ * Instantiates an ExtraRenderingInfo, by copying an existing one.
+ *
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link #ExtraRenderingInfo(ExtraRenderingInfo)} instead.
+ * @param other
+ */
+ @Deprecated
private static ExtraRenderingInfo obtain(ExtraRenderingInfo other) {
- ExtraRenderingInfo extraRenderingInfo = ExtraRenderingInfo.obtain();
- extraRenderingInfo.mLayoutSize = other.mLayoutSize;
- extraRenderingInfo.mTextSizeInPx = other.mTextSizeInPx;
- extraRenderingInfo.mTextSizeUnit = other.mTextSizeUnit;
- return extraRenderingInfo;
+ return new ExtraRenderingInfo(other);
}
/**
@@ -6268,14 +6139,13 @@
}
/**
- * Recycles this instance.
+ * Previously would recycle this instance.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- void recycle() {
- clear();
- sPool.release(this);
- }
+ @Deprecated
+ void recycle() {}
private void clear() {
mLayoutSize = null;
@@ -6291,7 +6161,7 @@
new Parcelable.Creator<AccessibilityNodeInfo>() {
@Override
public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
- AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ AccessibilityNodeInfo info = new AccessibilityNodeInfo();
info.initFromParcel(parcel);
return info;
}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index f26abb2..426a3f4 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -78,13 +78,6 @@
| AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS
| AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS;
- // Housekeeping
- private static final int MAX_POOL_SIZE = 10;
- private static final Object sPoolLock = new Object();
- private static AccessibilityRecord sPool;
- private static int sPoolSize;
- private AccessibilityRecord mNext;
- private boolean mIsInPool;
@UnsupportedAppUsage
boolean mSealed;
@@ -821,15 +814,14 @@
}
/**
- * Returns a cached instance if such is available or a new one is
- * instantiated. The instance is initialized with data from the
+ * Instantiates a new record initialized with data from the
* given record.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
- * constructor {@link #AccessibilityRecord(AccessibilityRecord)} instead.
- *
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link #AccessibilityRecord()} instead.
* @return An instance.
*/
+ @Deprecated
public static AccessibilityRecord obtain(AccessibilityRecord record) {
AccessibilityRecord clone = AccessibilityRecord.obtain();
clone.init(record);
@@ -837,51 +829,25 @@
}
/**
- * Returns a cached instance if such is available or a new one is
- * instantiated.
+ * Instantiates a new record.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityRecord()} instead.
- *
* @return An instance.
*/
+ @Deprecated
public static AccessibilityRecord obtain() {
- synchronized (sPoolLock) {
- if (sPool != null) {
- AccessibilityRecord record = sPool;
- sPool = sPool.mNext;
- sPoolSize--;
- record.mNext = null;
- record.mIsInPool = false;
- return record;
- }
- return new AccessibilityRecord();
- }
+ return new AccessibilityRecord();
}
/**
- * Return an instance back to be reused.
- * <p>
- * <strong>Note:</strong> You must not touch the object after calling this function.
+ * Would previously return an instance back to be reused.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
- *
- * @throws IllegalStateException If the record is already recycled.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- public void recycle() {
- if (mIsInPool) {
- throw new IllegalStateException("Record already recycled!");
- }
- clear();
- synchronized (sPoolLock) {
- if (sPoolSize <= MAX_POOL_SIZE) {
- mNext = sPool;
- sPool = this;
- mIsInPool = true;
- sPoolSize++;
- }
- }
- }
+ @Deprecated
+ public void recycle() { }
/**
* Initialize this record from another one.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1122056..bb13c1e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -472,6 +472,24 @@
public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT =
"augmented_service_request_timeout";
+ /**
+ * Sets allowed list for the autofill compatibility mode.
+ *
+ * The list of packages is {@code ":"} colon delimited, and each entry has the name of the
+ * package and an optional list of url bar resource ids (the list is delimited by
+ * brackets&mdash{@code [} and {@code ]}&mdash and is also comma delimited).
+ *
+ * <p>For example, a list with 3 packages {@code p1}, {@code p2}, and {@code p3}, where
+ * package {@code p1} have one id ({@code url_bar}, {@code p2} has none, and {@code p3 }
+ * have 2 ids {@code url_foo} and {@code url_bas}) would be
+ * {@code p1[url_bar]:p2:p3[url_foo,url_bas]}
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES =
+ "compat_mode_allowed_packages";
+
/** @hide */
public static final int RESULT_OK = 0;
/** @hide */
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0bb3cc2..3583cd4 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2093,6 +2093,10 @@
+ ", ic=" + ic + ", tba=" + tba + ", handler=" + icHandler);
}
view.onInputConnectionOpenedInternal(ic, tba, icHandler);
+ final ViewRootImpl viewRoot = view.getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.getHandwritingInitiator().onInputConnectionCreated(view, tba);
+ }
}
return true;
diff --git a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
index 60688ea..ba45b85 100644
--- a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
+++ b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
@@ -20,6 +20,7 @@
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
+import android.provider.DeviceConfig;
import java.util.Objects;
@@ -40,6 +41,11 @@
*/
public static final String LOG_TAG = "SelectionToolbar";
+ /**
+ * Whether system selection toolbar is enabled.
+ */
+ private static final String REMOTE_SELECTION_TOOLBAR_ENABLED =
+ "remote_selection_toolbar_enabled";
@NonNull
private final Context mContext;
@@ -86,4 +92,21 @@
throw e.rethrowFromSystemServer();
}
}
+
+ private boolean isRemoteSelectionToolbarEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SELECTION_TOOLBAR,
+ REMOTE_SELECTION_TOOLBAR_ENABLED, false);
+ }
+
+ /**
+ * Returns {@code true} if remote render selection toolbar enabled, otherwise
+ * returns {@code false}.
+ */
+ public static boolean isRemoteSelectionToolbarEnabled(Context context) {
+ SelectionToolbarManager manager = context.getSystemService(SelectionToolbarManager.class);
+ if (manager != null) {
+ return manager.isRemoteSelectionToolbarEnabled();
+ }
+ return false;
+ }
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a5f3feb..6d58ee2 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -301,6 +301,13 @@
public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
/**
+ * A ReadWriteHelper which has the same behavior as ReadWriteHelper.DEFAULT, but which is
+ * intentionally a different instance in order to trick Bundle reader so that it doesn't allow
+ * lazy initialization.
+ */
+ private static final Parcel.ReadWriteHelper ALTERNATIVE_DEFAULT = new Parcel.ReadWriteHelper();
+
+ /**
* Used to restrict the views which can be inflated
*
* @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
@@ -1856,7 +1863,18 @@
this.value = in.readTypedObject(Bitmap.CREATOR);
break;
case BUNDLE:
- this.value = in.readBundle();
+ // Because we use Parcel.allowSquashing() when writing, and that affects
+ // how the contents of Bundles are written, we need to ensure the bundle is
+ // unparceled immediately, not lazily. Setting a custom ReadWriteHelper
+ // just happens to have that effect on Bundle.readFromParcel().
+ // TODO(b/212731590): build this state tracking into Bundle
+ if (in.hasReadWriteHelper()) {
+ this.value = in.readBundle();
+ } else {
+ in.setReadWriteHelper(ALTERNATIVE_DEFAULT);
+ this.value = in.readBundle();
+ in.setReadWriteHelper(null);
+ }
break;
case INTENT:
this.value = in.readTypedObject(Intent.CREATOR);
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 862829b..dbf3570 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -74,6 +74,9 @@
* <p>
* Note that toasts being sent from the background are rate limited, so avoid sending such toasts
* in quick succession.
+ * <p>
+ * Starting with Android 12 (API level 31), apps targeting Android 12 or newer will have
+ * their toasts limited to two lines.
*
* <div class="special reference">
* <h3>Developer Guides</h3>
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index 090dbff..3f65f47 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -22,6 +22,7 @@
import android.annotation.SuppressLint;
import android.annotation.UiThread;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.content.Context;
@@ -48,13 +49,13 @@
*/
int SPLASH_SCREEN_STYLE_UNDEFINED = -1;
/**
- * Force splash screen to be empty.
- * @hide
+ * Flag to be used with {@link ActivityOptions#setSplashScreenStyle}, to avoid showing the
+ * splash screen icon of the launched activity
*/
int SPLASH_SCREEN_STYLE_EMPTY = 0;
/**
- * Force splash screen to show icon.
- * @hide
+ * Flag to be used with {@link ActivityOptions#setSplashScreenStyle}, to show the splash screen
+ * icon of the launched activity.
*/
int SPLASH_SCREEN_STYLE_ICON = 1;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index bc3c2f5..025f711 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -190,7 +190,7 @@
* the handover intent.
* TODO: investigate whether the privileged query is necessary to determine the availability.
*/
- protected static final String EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE =
+ public static final String EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE =
"com.android.internal.app.ChooserActivity.EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE";
/**
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 1d13b73f..587876d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -22,6 +22,7 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.ParcelFileDescriptor;
+import android.os.WakeLockStats;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.WifiActivityEnergyInfo;
@@ -157,6 +158,10 @@
/** {@hide} */
GpsBatteryStats getGpsBatteryStats();
+ /** {@hide} */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BATTERY_STATS)")
+ WakeLockStats getWakeLockStats();
+
HealthStatsParceler takeUidSnapshot(int uid);
HealthStatsParceler[] takeUidSnapshots(in int[] uid);
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 662ed6b..0470444 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -36,6 +36,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.KeyEvent;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.DumpableInputConnection;
@@ -350,8 +351,19 @@
}
if (handler.getLooper().isCurrentThread()) {
servedView.onInputConnectionClosedInternal();
+ final ViewRootImpl viewRoot = servedView.getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.getHandwritingInitiator().onInputConnectionClosed(servedView);
+ }
} else {
handler.post(servedView::onInputConnectionClosedInternal);
+ handler.post(() -> {
+ final ViewRootImpl viewRoot = servedView.getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.getHandwritingInitiator()
+ .onInputConnectionClosed(servedView);
+ }
+ });
}
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 879e0a8..b0fce8f 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -160,6 +160,11 @@
mHistoryDir = null;
mHistoryBuffer = historyBuffer;
}
+
+ public File getHistoryDirectory() {
+ return mHistoryDir;
+ }
+
/**
* Set the active file that mHistoryBuffer is backed up into.
*
@@ -375,12 +380,26 @@
}
/**
- * Read all history files and serialize into a big Parcel. This is to send history files to
- * Settings app since Settings app can not access /data/system directory.
- * Checkin file also call this method.
+ * Read all history files and serialize into a big Parcel.
+ * Checkin file calls this method.
+ *
* @param out the output parcel
*/
public void writeToParcel(Parcel out) {
+ writeToParcel(out, false /* useBlobs */);
+ }
+
+ /**
+ * This is for Settings app, when Settings app receives big history parcel, it call
+ * this method to parse it into list of parcels.
+ * @param out the output parcel
+ */
+ public void writeToBatteryUsageStatsParcel(Parcel out) {
+ out.writeBlob(mHistoryBuffer.marshall());
+ writeToParcel(out, true /* useBlobs */);
+ }
+
+ private void writeToParcel(Parcel out, boolean useBlobs) {
final long start = SystemClock.uptimeMillis();
out.writeInt(mFileNumbers.size() - 1);
for(int i = 0; i < mFileNumbers.size() - 1; i++) {
@@ -391,7 +410,12 @@
} catch(Exception e) {
Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e);
}
- out.writeByteArray(raw);
+ if (useBlobs) {
+ out.writeBlob(raw);
+ } else {
+ // Avoiding blobs in the check-in file for compatibility
+ out.writeByteArray(raw);
+ }
}
if (DEBUG) {
Slog.d(TAG, "writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start));
@@ -399,18 +423,36 @@
}
/**
- * This is for Settings app, when Settings app receives big history parcel, it call
- * this method to parse it into list of parcels.
- * Checkin file also call this method.
+ * Reads a BatteryStatsHistory from a parcel written with
+ * the {@link #writeToBatteryUsageStatsParcel} method.
+ */
+ public static BatteryStatsHistory createFromBatteryUsageStatsParcel(Parcel in) {
+ final byte[] historyBlob = in.readBlob();
+
+ Parcel historyBuffer = Parcel.obtain();
+ historyBuffer.unmarshall(historyBlob, 0, historyBlob.length);
+
+ BatteryStatsHistory history = new BatteryStatsHistory(historyBuffer);
+ history.readFromParcel(in, true /* useBlobs */);
+ return history;
+ }
+
+ /**
+ * This is for the check-in file, which has all history files embedded.
+ *
* @param in the input parcel.
*/
public void readFromParcel(Parcel in) {
+ readFromParcel(in, false /* useBlobs */);
+ }
+
+ private void readFromParcel(Parcel in, boolean useBlobs) {
final long start = SystemClock.uptimeMillis();
mHistoryParcels = new ArrayList<>();
final int count = in.readInt();
for(int i = 0; i < count; i++) {
- byte[] temp = in.createByteArray();
- if (temp.length == 0) {
+ byte[] temp = useBlobs ? in.readBlob() : in.createByteArray();
+ if (temp == null || temp.length == 0) {
continue;
}
Parcel p = Parcel.obtain();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 209c64a..21f719c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -60,6 +60,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.WakeLockStats;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.os.connectivity.CellularBatteryStats;
@@ -1143,6 +1144,37 @@
return mKernelWakelockStats;
}
+ @Override
+ public WakeLockStats getWakeLockStats() {
+ final long realtimeMs = mClock.elapsedRealtime();
+ final long realtimeUs = realtimeMs * 1000;
+ List<WakeLockStats.WakeLock> uidWakeLockStats = new ArrayList<>();
+ for (int i = mUidStats.size() - 1; i >= 0; i--) {
+ final Uid uid = mUidStats.valueAt(i);
+ final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
+ uid.mWakelockStats.getMap();
+ for (int j = wakelockStats.size() - 1; j >= 0; j--) {
+ final String name = wakelockStats.keyAt(j);
+ final Uid.Wakelock wakelock = (Uid.Wakelock) wakelockStats.valueAt(j);
+ final DualTimer timer = wakelock.mTimerPartial;
+ if (timer != null) {
+ final long totalTimeLockHeldMs =
+ timer.getTotalTimeLocked(realtimeUs, STATS_SINCE_CHARGED) / 1000;
+ if (totalTimeLockHeldMs != 0) {
+ uidWakeLockStats.add(
+ new WakeLockStats.WakeLock(uid.getUid(), name,
+ timer.getCountLocked(STATS_SINCE_CHARGED),
+ totalTimeLockHeldMs,
+ timer.isRunningLocked()
+ ? timer.getCurrentDurationMsLocked(realtimeMs)
+ : 0));
+ }
+ }
+ }
+ }
+ return new WakeLockStats(uidWakeLockStats);
+ }
+
String mLastWakeupReason = null;
long mLastWakeupUptimeMs = 0;
private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 88425be..69b7b4e 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -21,13 +21,16 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.Parcel;
import android.os.SystemClock;
import android.os.UidBatteryConsumer;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -133,9 +136,12 @@
*/
@VisibleForTesting
public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
- return getBatteryUsageStats(query, currentTimeMillis());
+ synchronized (mStats) {
+ return getBatteryUsageStats(query, currentTimeMillis());
+ }
}
+ @GuardedBy("mStats")
private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
long currentTimeMs) {
if (query.getToTimestamp() == 0) {
@@ -145,6 +151,7 @@
}
}
+ @GuardedBy("mStats")
private BatteryUsageStats getCurrentBatteryUsageStats(BatteryUsageStatsQuery query,
long currentTimeMs) {
final long realtimeUs = elapsedRealtime() * 1000;
@@ -189,7 +196,18 @@
}
BatteryStatsImpl batteryStatsImpl = (BatteryStatsImpl) mStats;
- batteryUsageStatsBuilder.setBatteryHistory(batteryStatsImpl.mHistoryBuffer);
+
+ // Make a copy of battery history to avoid concurrent modification.
+ Parcel historyBuffer = Parcel.obtain();
+ historyBuffer.appendFrom(batteryStatsImpl.mHistoryBuffer, 0,
+ batteryStatsImpl.mHistoryBuffer.dataSize());
+
+ final File systemDir =
+ batteryStatsImpl.mBatteryStatsHistory.getHistoryDirectory().getParentFile();
+ final BatteryStatsHistory batteryStatsHistory =
+ new BatteryStatsHistory(batteryStatsImpl, systemDir, historyBuffer);
+
+ batteryUsageStatsBuilder.setBatteryHistory(batteryStatsHistory);
}
return batteryUsageStatsBuilder.build();
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 6541b14..ba7a0ef 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -121,7 +121,7 @@
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.DecorCaptionView;
-import com.android.internal.widget.FloatingToolbar;
+import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import java.util.List;
import java.util.function.Consumer;
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 1db7426..3c6b7ff 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -162,4 +162,20 @@
void requestAddTile(in ComponentName componentName, in CharSequence label, in Icon icon, int userId, in IAddTileResultCallback callback);
void cancelRequestAddTile(in String packageName);
+
+ /**
+ * Overrides the navigation bar mode.
+ *
+ * @param navBarModeOverride the mode of the navigation bar override to be set.
+ *
+ * @hide
+ */
+ void setNavBarModeOverride(int navBarModeOverride);
+
+ /**
+ * Gets the navigation bar mode override.
+ *
+ * @hide
+ */
+ int getNavBarModeOverride();
}
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 36913b7..06e69f2 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -33,7 +33,7 @@
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.widget.FloatingToolbar;
+import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import java.util.Arrays;
import java.util.Objects;
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbar.java
similarity index 88%
rename from core/java/com/android/internal/widget/FloatingToolbar.java
rename to core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbar.java
index a0bf9b5..e75f372 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbar.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.widget;
+package com.android.internal.widget.floatingtoolbar;
import android.annotation.Nullable;
import android.graphics.Rect;
@@ -50,14 +50,10 @@
private final FloatingToolbarPopup mPopup;
private final Rect mContentRect = new Rect();
- private final Rect mPreviousContentRect = new Rect();
private Menu mMenu;
private MenuItem.OnMenuItemClickListener mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
- private int mSuggestedWidth;
- private boolean mWidthChanged = true;
-
private final OnLayoutChangeListener mOrientationChangeHandler = new OnLayoutChangeListener() {
private final Rect mNewRect = new Rect();
@@ -71,7 +67,7 @@
mNewRect.set(newLeft, newRight, newTop, newBottom);
mOldRect.set(oldLeft, oldRight, oldTop, oldBottom);
if (mPopup.isShowing() && !mNewRect.equals(mOldRect)) {
- mWidthChanged = true;
+ mPopup.setWidthChanged(true);
updateLayout();
}
}
@@ -114,7 +110,7 @@
// TODO(b/65172902): Pass context in constructor when DecorView (and other callers)
// supports multi-display.
mWindow = Objects.requireNonNull(window);
- mPopup = new FloatingToolbarPopup(window.getContext(), window.getDecorView());
+ mPopup = FloatingToolbarPopup.createInstance(window.getContext(), window.getDecorView());
}
/**
@@ -159,11 +155,7 @@
* toolbar.
*/
public FloatingToolbar setSuggestedWidth(int suggestedWidth) {
- // Check if there's been a substantial width spec change.
- int difference = Math.abs(suggestedWidth - mSuggestedWidth);
- mWidthChanged = difference > (mSuggestedWidth * 0.2);
-
- mSuggestedWidth = suggestedWidth;
+ mPopup.setSuggestedWidth(suggestedWidth);
return this;
}
@@ -232,19 +224,7 @@
private void doShow() {
List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
menuItems.sort(mMenuItemComparator);
- if (mPopup.isLayoutRequired(menuItems) || mWidthChanged) {
- mPopup.dismiss();
- mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth);
- } else {
- mPopup.updateMenuItems(menuItems, mMenuItemClickListener);
- }
- if (!mPopup.isShowing()) {
- mPopup.show(mContentRect);
- } else if (!mPreviousContentRect.equals(mContentRect)) {
- mPopup.updateCoordinates(mContentRect);
- }
- mWidthChanged = false;
- mPreviousContentRect.set(mContentRect);
+ mPopup.show(menuItems, mMenuItemClickListener, mContentRect);
}
/**
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java
new file mode 100644
index 0000000..f47700c
--- /dev/null
+++ b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget.floatingtoolbar;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.selectiontoolbar.SelectionToolbarManager;
+import android.widget.PopupWindow;
+
+import java.util.List;
+
+/**
+ * A popup window used by the {@link FloatingToolbar} to render menu items.
+ *
+ */
+public interface FloatingToolbarPopup {
+
+ /**
+ * Sets the suggested dp width of this floating toolbar.
+ * The actual width will be about this size but there are no guarantees that it will be exactly
+ * the suggested width.
+ */
+ void setSuggestedWidth(int suggestedWidth);
+
+ /**
+ * Sets if the floating toolbar width changed.
+ */
+ void setWidthChanged(boolean widthChanged);
+
+ /**
+ * Shows this popup at the specified coordinates.
+ * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+ */
+ void show(List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener,
+ Rect contentRect);
+
+ /**
+ * Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
+ */
+ void dismiss();
+
+ /**
+ * Hides this popup. This is a no-op if this popup is not showing.
+ * Use {@link #isHidden()} to distinguish between a hidden and a dismissed popup.
+ */
+ void hide();
+
+ /**
+ * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
+ */
+ boolean isShowing();
+
+ /**
+ * Returns {@code true} if this popup is currently hidden. {@code false} otherwise.
+ */
+ boolean isHidden();
+
+ /**
+ * Makes this toolbar "outside touchable" and sets the onDismissListener.
+ *
+ * @param outsideTouchable if true, the popup will be made "outside touchable" and
+ * "non focusable". The reverse will happen if false.
+ * @param onDismiss
+ *
+ * @return true if the "outsideTouchable" setting was modified. Otherwise returns false
+ *
+ * @see PopupWindow#setOutsideTouchable(boolean)
+ * @see PopupWindow#setFocusable(boolean)
+ * @see PopupWindow.OnDismissListener
+ */
+ boolean setOutsideTouchable(boolean outsideTouchable, PopupWindow.OnDismissListener onDismiss);
+
+ /**
+ * Returns {@link RemoteFloatingToolbarPopup} implementation if the system selection toolbar
+ * enabled, otherwise returns {@link LocalFloatingToolbarPopup} implementation.
+ */
+ static FloatingToolbarPopup createInstance(Context context, View parent) {
+ boolean enabled = SelectionToolbarManager.isRemoteSelectionToolbarEnabled(context);
+ return enabled
+ ? new RemoteFloatingToolbarPopup(context, parent)
+ : new LocalFloatingToolbarPopup(context, parent);
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/FloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
similarity index 96%
rename from core/java/com/android/internal/widget/FloatingToolbarPopup.java
rename to core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
index e0388f6..80d8bd7 100644
--- a/core/java/com/android/internal/widget/FloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.widget;
+package com.android.internal.widget.floatingtoolbar;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -70,13 +70,13 @@
import java.util.Objects;
/**
- * A popup window used by the floating toolbar.
+ * A popup window used by the floating toolbar to render menu items in the local app process.
*
* This class is responsible for the rendering/animation of the floating toolbar.
* It holds 2 panels (i.e. main panel and overflow panel) and an overflow button
* to transition between panels.
*/
-public final class FloatingToolbarPopup {
+public final class LocalFloatingToolbarPopup implements FloatingToolbarPopup {
/* Minimum and maximum number of items allowed in the overflow. */
private static final int MIN_OVERFLOW_SIZE = 2;
@@ -94,7 +94,7 @@
private final ViewGroup mContentContainer; // holds all contents.
private final ViewGroup mMainPanel; // holds menu items that are initially displayed.
// holds menu items hidden in the overflow.
- private final FloatingToolbarPopup.OverflowPanel mOverflowPanel;
+ private final OverflowPanel mOverflowPanel;
private final ImageButton mOverflowButton; // opens/closes the overflow.
/* overflow button drawables. */
private final Drawable mArrow;
@@ -102,8 +102,7 @@
private final AnimatedVectorDrawable mToArrow;
private final AnimatedVectorDrawable mToOverflow;
- private final FloatingToolbarPopup.OverflowPanelViewHelper
- mOverflowPanelViewHelper;
+ private final OverflowPanelViewHelper mOverflowPanelViewHelper;
/* Animation interpolators. */
private final Interpolator mLogAccelerateInterpolator;
@@ -138,7 +137,7 @@
private final int mIconTextSpacing;
/**
- * @see FloatingToolbarPopup.OverflowPanelViewHelper#preparePopupContent().
+ * @see OverflowPanelViewHelper#preparePopupContent().
*/
private final Runnable mPreparePopupContentRTLHelper = new Runnable() {
@Override
@@ -184,16 +183,20 @@
private int mTransitionDurationScale; // Used to scale the toolbar transition duration.
+ private final Rect mPreviousContentRect = new Rect();
+ private int mSuggestedWidth;
+ private boolean mWidthChanged = true;
+
/**
* Initializes a new floating toolbar popup.
*
* @param parent A parent view to get the {@link android.view.View#getWindowToken()} token
* from.
*/
- public FloatingToolbarPopup(Context context, View parent) {
+ public LocalFloatingToolbarPopup(Context context, View parent) {
mParent = Objects.requireNonNull(parent);
mContext = applyDefaultTheme(context);
- mContentContainer = createContentContainer(context);
+ mContentContainer = createContentContainer(mContext);
mPopupWindow = createPopupWindow(mContentContainer);
mMarginHorizontal = parent.getResources()
.getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
@@ -205,7 +208,7 @@
.getDimensionPixelSize(R.dimen.floating_toolbar_icon_text_spacing);
// Interpolators
- mLogAccelerateInterpolator = new FloatingToolbarPopup.LogAccelerateInterpolator();
+ mLogAccelerateInterpolator = new LogAccelerateInterpolator();
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
mContext, android.R.interpolator.fast_out_slow_in);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
@@ -231,8 +234,7 @@
mOverflowButton = createOverflowButton();
mOverflowButtonSize = measure(mOverflowButton);
mMainPanel = createMainPanel();
- mOverflowPanelViewHelper =
- new FloatingToolbarPopup.OverflowPanelViewHelper(mContext, mIconTextSpacing);
+ mOverflowPanelViewHelper = new OverflowPanelViewHelper(mContext, mIconTextSpacing);
mOverflowPanel = createOverflowPanel();
// Animation. Need views.
@@ -263,19 +265,7 @@
});
}
- /**
- * Makes this toolbar "outside touchable" and sets the onDismissListener.
- *
- * @param outsideTouchable if true, the popup will be made "outside touchable" and
- * "non focusable". The reverse will happen if false.
- * @param onDismiss
- *
- * @return true if the "outsideTouchable" setting was modified. Otherwise returns false
- *
- * @see PopupWindow#setOutsideTouchable(boolean)
- * @see PopupWindow#setFocusable(boolean)
- * @see PopupWindow.OnDismissListener
- */
+ @Override
public boolean setOutsideTouchable(
boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
boolean ret = false;
@@ -293,7 +283,7 @@
* Lays out buttons for the specified menu items.
* Requires a subsequent call to {@link FloatingToolbar#show()} to show the items.
*/
- public void layoutMenuItems(
+ private void layoutMenuItems(
List<MenuItem> menuItems,
MenuItem.OnMenuItemClickListener menuItemClickListener,
int suggestedWidth) {
@@ -314,7 +304,7 @@
*
* @see #isLayoutRequired(List<MenuItem>)
*/
- public void updateMenuItems(
+ private void updateMenuItems(
List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener) {
mMenuItems.clear();
for (MenuItem menuItem : menuItems) {
@@ -326,15 +316,42 @@
/**
* Returns true if this popup needs a relayout to properly render the specified menu items.
*/
- public boolean isLayoutRequired(List<MenuItem> menuItems) {
+ private boolean isLayoutRequired(List<MenuItem> menuItems) {
return !MenuItemRepr.reprEquals(menuItems, mMenuItems.values());
}
- /**
- * Shows this popup at the specified coordinates.
- * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
- */
- public void show(Rect contentRectOnScreen) {
+ @Override
+ public void setWidthChanged(boolean widthChanged) {
+ mWidthChanged = widthChanged;
+ }
+
+ @Override
+ public void setSuggestedWidth(int suggestedWidth) {
+ // Check if there's been a substantial width spec change.
+ int difference = Math.abs(suggestedWidth - mSuggestedWidth);
+ mWidthChanged = difference > (mSuggestedWidth * 0.2);
+ mSuggestedWidth = suggestedWidth;
+ }
+
+ @Override
+ public void show(List<MenuItem> menuItems,
+ MenuItem.OnMenuItemClickListener menuItemClickListener, Rect contentRect) {
+ if (isLayoutRequired(menuItems) || mWidthChanged) {
+ dismiss();
+ layoutMenuItems(menuItems, menuItemClickListener, mSuggestedWidth);
+ } else {
+ updateMenuItems(menuItems, menuItemClickListener);
+ }
+ if (!isShowing()) {
+ show(contentRect);
+ } else if (!mPreviousContentRect.equals(contentRect)) {
+ updateCoordinates(contentRect);
+ }
+ mWidthChanged = false;
+ mPreviousContentRect.set(contentRect);
+ }
+
+ private void show(Rect contentRectOnScreen) {
Objects.requireNonNull(contentRectOnScreen);
if (isShowing()) {
@@ -357,9 +374,7 @@
runShowAnimation();
}
- /**
- * Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
- */
+ @Override
public void dismiss() {
if (mDismissed) {
return;
@@ -373,10 +388,7 @@
setZeroTouchableSurface();
}
- /**
- * Hides this popup. This is a no-op if this popup is not showing.
- * Use {@link #isHidden()} to distinguish between a hidden and a dismissed popup.
- */
+ @Override
public void hide() {
if (!isShowing()) {
return;
@@ -387,16 +399,12 @@
setZeroTouchableSurface();
}
- /**
- * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
- */
+ @Override
public boolean isShowing() {
return !mDismissed && !mHidden;
}
- /**
- * Returns {@code true} if this popup is currently hidden. {@code false} otherwise.
- */
+ @Override
public boolean isHidden() {
return mHidden;
}
@@ -406,7 +414,7 @@
* The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
* This is a no-op if this popup is not showing.
*/
- public void updateCoordinates(Rect contentRectOnScreen) {
+ private void updateCoordinates(Rect contentRectOnScreen) {
Objects.requireNonNull(contentRectOnScreen);
if (!isShowing() || !mPopupWindow.isShowing()) {
@@ -1206,9 +1214,8 @@
return overflowButton;
}
- private FloatingToolbarPopup.OverflowPanel createOverflowPanel() {
- final FloatingToolbarPopup.OverflowPanel
- overflowPanel = new FloatingToolbarPopup.OverflowPanel(this);
+ private OverflowPanel createOverflowPanel() {
+ final OverflowPanel overflowPanel = new OverflowPanel(this);
overflowPanel.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
overflowPanel.setDivider(null);
@@ -1307,9 +1314,9 @@
*/
private static final class OverflowPanel extends ListView {
- private final FloatingToolbarPopup mPopup;
+ private final LocalFloatingToolbarPopup mPopup;
- OverflowPanel(FloatingToolbarPopup popup) {
+ OverflowPanel(LocalFloatingToolbarPopup popup) {
super(Objects.requireNonNull(popup).mContext);
this.mPopup = popup;
setScrollBarDefaultDelayBeforeFade(ViewConfiguration.getScrollDefaultDelay() * 3);
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/OWNERS b/core/java/com/android/internal/widget/floatingtoolbar/OWNERS
new file mode 100644
index 0000000..ed9425c
--- /dev/null
+++ b/core/java/com/android/internal/widget/floatingtoolbar/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/selectiontoolbar/OWNERS
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java
new file mode 100644
index 0000000..b3a8128
--- /dev/null
+++ b/core/java/com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget.floatingtoolbar;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.selectiontoolbar.SelectionToolbarManager;
+import android.widget.PopupWindow;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A popup window used by the floating toolbar to render menu items in the remote system process.
+ *
+ * It holds 2 panels (i.e. main panel and overflow panel) and an overflow button
+ * to transition between panels.
+ */
+public final class RemoteFloatingToolbarPopup implements FloatingToolbarPopup {
+
+ private final SelectionToolbarManager mSelectionToolbarManager;
+ // Parent for the popup window.
+ private final View mParent;
+
+ public RemoteFloatingToolbarPopup(Context context, View parent) {
+ // TODO: implement it
+ mParent = Objects.requireNonNull(parent);
+ mSelectionToolbarManager = context.getSystemService(SelectionToolbarManager.class);
+ }
+
+ @Override
+ public void show(List<MenuItem> menuItems,
+ MenuItem.OnMenuItemClickListener menuItemClickListener, Rect contentRect) {
+ // TODO: implement it
+ }
+
+ @Override
+ public void hide() {
+ // TODO: implement it
+ }
+
+ @Override
+ public void setSuggestedWidth(int suggestedWidth) {
+ // TODO: implement it
+ }
+
+ @Override
+ public void setWidthChanged(boolean widthChanged) {
+ // no-op
+ }
+
+ @Override
+ public void dismiss() {
+ // TODO: implement it
+ }
+
+ @Override
+ public boolean isHidden() {
+ return false;
+ }
+
+ @Override
+ public boolean isShowing() {
+ return false;
+ }
+
+ @Override
+ public boolean setOutsideTouchable(boolean outsideTouchable,
+ PopupWindow.OnDismissListener onDismiss) {
+ return false;
+ }
+}
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
index 26ff192..d89566c9 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -70,8 +70,8 @@
Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
+ Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
}
- if (options.statsTag == -1 && StrictMode.vmUntaggedSocketEnabled()) {
- StrictMode.onUntaggedSocket();
+ if (options.statsTag == -1) {
+ StrictMode.noteUntaggedSocket();
}
// TODO: skip tagging when options would be no-op
tagSocketFd(fd, options.statsTag, options.statsUid);
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index d66f461..39f17e5 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -849,8 +849,8 @@
XmlUtils.skipCurrentTag(parser);
}
} break;
- case "updatable-library":
- // "updatable-library" is meant to behave exactly like "library"
+ case "apex-library":
+ // "apex-library" is meant to behave exactly like "library"
case "library": {
if (allowLibs) {
String lname = parser.getAttributeValue(null, "name");
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index 0eb8c6a..011e051 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -83,17 +83,20 @@
constexpr int INDIC_MIN_PREFIX = 2;
constexpr int INDIC_MIN_SUFFIX = 2;
+ addHyphenator("af", 1, 1); // Afrikaans
addHyphenator("am", 1, 1); // Amharic
addHyphenator("as", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Assamese
addHyphenator("be", 2, 2); // Belarusian
addHyphenator("bg", 2, 2); // Bulgarian
addHyphenator("bn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Bengali
+ addHyphenator("cs", 2, 2); // Czech
addHyphenator("cu", 1, 2); // Church Slavonic
addHyphenator("cy", 2, 3); // Welsh
addHyphenator("da", 2, 2); // Danish
addHyphenator("de-1901", 2, 2); // German 1901 orthography
addHyphenator("de-1996", 2, 2); // German 1996 orthography
addHyphenator("de-CH-1901", 2, 2); // Swiss High German 1901 orthography
+ addHyphenator("el", 1, 1); // Greek
addHyphenator("en-GB", 2, 3); // British English
addHyphenator("en-US", 2, 3); // American English
addHyphenator("es", 2, 2); // Spanish
@@ -110,18 +113,23 @@
// Going with a more conservative value of (2, 2) for now.
addHyphenator("hy", 2, 2); // Armenian
addHyphenator("it", 2, 2); // Italian
+ addHyphenator("ka", 1, 2); // Georgian
addHyphenator("kn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Kannada
addHyphenator("la", 2, 2); // Latin
addHyphenator("lt", 2, 2); // Lithuanian
+ addHyphenator("lv", 2, 2); // Latvian
addHyphenator("ml", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Malayalam
addHyphenator("mn-Cyrl", 2, 2); // Mongolian in Cyrillic script
addHyphenator("mr", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Marathi
addHyphenator("nb", 2, 2); // Norwegian Bokmål
+ addHyphenator("nl", 2, 2); // Dutch
addHyphenator("nn", 2, 2); // Norwegian Nynorsk
addHyphenator("or", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Oriya
addHyphenator("pa", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Punjabi
addHyphenator("pt", 2, 3); // Portuguese
+ addHyphenator("sk", 2, 2); // Slovak
addHyphenator("sl", 2, 2); // Slovenian
+ addHyphenator("sq", 2, 2); // Albanian
addHyphenator("ta", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Tamil
addHyphenator("te", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Telugu
addHyphenator("tk", 2, 2); // Turkmen
diff --git a/core/proto/android/inputmethodservice/softinputwindow.proto b/core/proto/android/inputmethodservice/softinputwindow.proto
index 85b7d73..e0ba6bf 100644
--- a/core/proto/android/inputmethodservice/softinputwindow.proto
+++ b/core/proto/android/inputmethodservice/softinputwindow.proto
@@ -23,10 +23,10 @@
option java_multiple_files = true;
message SoftInputWindowProto {
- optional string name = 1;
- optional int32 window_type = 2;
- optional int32 gravity = 3;
- optional bool takes_focus = 4;
+ reserved 1; // name
+ reserved 2; // window_type
+ reserved 3; // gravity
+ reserved 4; // takes_focus
optional .android.graphics.RectProto bounds = 5;
optional int32 window_state = 6;
}
\ No newline at end of file
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index ba4a5b0..00b7fd7 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -85,6 +85,7 @@
optional SettingProto accessibility_floating_menu_icon_type = 39 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_floating_menu_opacity = 40 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_floating_menu_fade_enabled = 41 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto odi_captions_volume_ui_enabled = 42 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
@@ -382,6 +383,7 @@
optional SettingProto multi_press_timeout = 38 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto nav_bar_kids_mode = 91 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto navigation_mode = 76 [ (android.privacy).dest = DEST_AUTOMATIC ];
message NfcPayment {
@@ -664,5 +666,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 91;
+ // Next tag = 92;
}
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index 48feb4d..1dedbb9 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -112,6 +112,8 @@
optional string last_disabled_app_caller = 8;
repeated string suspending_package = 9;
optional int32 distraction_flags = 10;
+ // UTC timestamp of first install for the user
+ optional int32 first_install_time_ms = 11;
}
message InstallSourceProto {
@@ -147,8 +149,7 @@
optional int32 version_code = 3;
// Package's reported version string (what's displayed to the user).
optional string version_string = 4;
- // UTC timestamp of install
- optional int64 install_time_ms = 5;
+ reserved 5;
// Millisecond UTC timestamp of latest update adjusted to Google's server clock.
optional int64 update_time_ms = 6;
// From "dumpsys package" - name of package which installed this one.
diff --git a/core/proto/android/view/imeinsetssourceconsumer.proto b/core/proto/android/view/imeinsetssourceconsumer.proto
index 1b9aff9..b1ed365 100644
--- a/core/proto/android/view/imeinsetssourceconsumer.proto
+++ b/core/proto/android/view/imeinsetssourceconsumer.proto
@@ -16,7 +16,6 @@
syntax = "proto2";
-import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto";
import "frameworks/base/core/proto/android/view/insetssourceconsumer.proto";
package android.view;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d0fee11..a005eb0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -714,6 +714,9 @@
<protected-broadcast android:name="android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED" />
<protected-broadcast android:name="android.app.action.SHOW_NEW_USER_DISCLAIMER" />
+ <!-- Added in T -->
+ <protected-broadcast android:name="android.intent.action.REFRESH_SAFETY_SOURCES" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -1499,7 +1502,7 @@
<p>Protection level: dangerous
<p> This is a hard restricted permission which cannot be held by an app until
- the installer on record whitelists the permission. For more details see
+ the installer on record allowlists the permission. For more details see
{@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
-->
<permission android:name="android.permission.BODY_SENSORS_BACKGROUND"
@@ -5479,6 +5482,16 @@
android:protectionLevel="signature|installer" />
<!--
+ @SystemApi
+ Allows the holder to start the screen to review permission decisions.
+ <p>Protection level: signature|installer
+ @hide -->
+ <permission android:name="android.permission.START_REVIEW_PERMISSION_DECISIONS"
+ android:label="@string/permlab_startReviewPermissionDecisions"
+ android:description="@string/permdesc_startReviewPermissionDecisions"
+ android:protectionLevel="signature|installer" />
+
+ <!--
Allows the holder to start the screen with a list of app features.
<p>Protection level: signature|installer
-->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index bfc1c83..1705371 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8440,6 +8440,8 @@
<!-- Component name of an activity that allows the user to modify
the settings for this dream. -->
<attr name="settingsActivity" />
+ <!-- A preview, in a drawable resource id, of what the Dream will look like. -->
+ <attr name="previewImage" format="reference" />
</declare-styleable>
<!-- Use <code>trust-agent</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 06f347f..db24475 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3070,7 +3070,6 @@
<declare-styleable name="AndroidManifestMetaData"
parent="AndroidManifestApplication
AndroidManifestActivity
- AndroidManifestApexSystemService
AndroidManifestReceiver
AndroidManifestProvider
AndroidManifestService
@@ -3105,7 +3104,6 @@
<declare-styleable name="AndroidManifestProperty"
parent="AndroidManifestApplication
AndroidManifestActivity
- AndroidManifestApexSystemService
AndroidManifestReceiver
AndroidManifestProvider
AndroidManifestService">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bd0604e..7d8bcea 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2897,6 +2897,10 @@
<!-- Make the IME killable by the lowmemorykiller by raising its oom_score_adj. -->
<bool name="config_killableInputMethods">false</bool>
+ <!-- Prevent the InputMethodManagerService from starting up the IME unless
+ the currently focused view is a text editor. -->
+ <bool name="config_preventImeStartupUnlessTextEditor">false</bool>
+
<!-- The list of classes that should be added to the notification ranking pipeline.
See {@link com.android.server.notification.NotificationSignalExtractor}
If you add a new extractor to this list make sure to update
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 1edaaa5..9449f60 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3282,6 +3282,8 @@
</staging-public-group>
<staging-public-group type="array" first-id="0x01d90000">
+ <!-- @hide @SystemApi -->
+ <public name="config_optionalIpSecAlgorithms" />
</staging-public-group>
<staging-public-group type="drawable" first-id="0x01d80000">
@@ -3314,6 +3316,8 @@
<staging-public-group type="bool" first-id="0x01cf0000">
<!-- @hide @SystemApi -->
<public name="config_systemCaptionsServiceCallsEnabled" />
+ <!-- @hide @TestApi -->
+ <public name="config_preventImeStartupUnlessTextEditor" />
</staging-public-group>
<staging-public-group type="fraction" first-id="0x01ce0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1f5f189..2879759 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2012,6 +2012,11 @@
<string name="permdesc_startViewPermissionUsage">Allows the holder to start the permission usage for an app. Should never be needed for normal apps.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permlab_startReviewPermissionDecisions">start view permission decisions</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permdesc_startReviewPermissionDecisions">Allows the holder to start screen to review permission decisions. Should never be needed for normal apps.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
<string name="permlab_startViewAppFeatures">start view app features</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
<string name="permdesc_startViewAppFeatures">Allows the holder to start viewing the features info for an app.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7687b93..ba4aa81 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2250,6 +2250,7 @@
<java-symbol type="bool" name="config_autoResetAirplaneMode" />
<java-symbol type="string" name="config_notificationAccessConfirmationActivity" />
<java-symbol type="bool" name="config_killableInputMethods" />
+ <java-symbol type="bool" name="config_preventImeStartupUnlessTextEditor" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 2c3c1ed..9bb064c 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -25,6 +25,7 @@
import android.content.pm.PackageInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
import androidx.test.filters.LargeTest;
@@ -39,6 +40,7 @@
import java.util.Arrays;
import java.util.List;
+@Presubmit
@LargeTest
public class ApplicationPackageManagerTest extends TestCase {
private static final String sInternalVolPath = "/data";
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
index ceebc62..c9a6d22 100644
--- a/core/tests/coretests/src/android/content/ContentProviderTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.java
@@ -24,6 +24,7 @@
import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
@@ -32,6 +33,7 @@
import org.junit.runner.RunWith;
import org.mockito.Answers;
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class ContentProviderTest {
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 01e240a..7b70b41 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -35,6 +35,7 @@
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
import android.util.Size;
import androidx.test.InstrumentationRegistry;
@@ -45,6 +46,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class ContentResolverTest {
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index 3d7d807..e4a9ce5 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -34,6 +34,7 @@
import android.hardware.display.VirtualDisplay;
import android.media.ImageReader;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.view.Display;
import androidx.test.core.app.ApplicationProvider;
@@ -48,6 +49,7 @@
* Build/Install/Run:
* atest FrameworksCoreTests:ContextTest
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ContextTest {
diff --git a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
index 1bc46a7..68d4cd4 100644
--- a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
@@ -20,6 +20,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.LargeTest;
@@ -36,6 +37,7 @@
* Run: adb shell am instrument -e class android.content.ManagedUserContentResolverTest -w \
* com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
*/
+@Presubmit
@LargeTest
public class ManagedUserContentResolverTest extends AbstractCrossUserContentResolverTest {
@Override
diff --git a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
index dbe0278..de4c572 100644
--- a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
@@ -18,6 +18,7 @@
import android.content.pm.UserInfo;
import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.LargeTest;
@@ -34,6 +35,7 @@
* Run: adb shell am instrument -e class android.content.SecondaryUserContentResolverTest -w \
* com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
*/
+@Presubmit
@LargeTest
public class SecondaryUserContentResolverTest extends AbstractCrossUserContentResolverTest {
@Override
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
index 7ccbb01..e6660f3 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
@@ -170,7 +170,7 @@
when(mFile.getUsableSpace()).thenReturn(10000L);
when(mFile.getTotalSpace()).thenReturn(100000L);
long result = mSm.getStorageCacheBytes(mFile, 0);
- assertThat(result).isEqualTo(4666L);
+ assertThat(result).isEqualTo(4667L);
}
/**
diff --git a/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
new file mode 100644
index 0000000..8f04461
--- /dev/null
+++ b/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link HandwritingInitiator}
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:HandwritingInitiatorTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HandwritingInitiatorTest {
+ private static final int TOUCH_SLOP = 8;
+ private static final long TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
+ private static final Rect sHwArea = new Rect(100, 200, 500, 500);
+ private static final EditorInfo sFakeEditorInfo = new EditorInfo();
+
+ private HandwritingInitiator mHandwritingInitiator;
+ private View mTestView;
+
+ @Before
+ public void setup() {
+ final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ Context context = mInstrumentation.getTargetContext();
+ ViewConfiguration viewConfiguration = mock(ViewConfiguration.class);
+ when(viewConfiguration.getScaledTouchSlop()).thenReturn(TOUCH_SLOP);
+
+ InputMethodManager inputMethodManager = context.getSystemService(InputMethodManager.class);
+ mHandwritingInitiator =
+ spy(new HandwritingInitiator(viewConfiguration, inputMethodManager));
+ mHandwritingInitiator.updateEditorBound(sHwArea);
+
+ // mock a parent so that HandwritingInitiator can get
+ ViewGroup parent = new ViewGroup(context) {
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // We don't layout this view.
+ }
+ @Override
+ public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
+ r.left = sHwArea.left;
+ r.top = sHwArea.top;
+ r.right = sHwArea.right;
+ r.bottom = sHwArea.bottom;
+ return true;
+ }
+ };
+
+ mTestView = mock(View.class);
+ when(mTestView.isAttachedToWindow()).thenReturn(true);
+ parent.addView(mTestView);
+ }
+
+ @Test
+ public void onTouchEvent_startHandwriting_when_stylusMoveOnce_withinHWArea() {
+ mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ final int x1 = (sHwArea.left + sHwArea.right) / 2;
+ final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
+ MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+ final int x2 = x1 + TOUCH_SLOP * 2;
+ final int y2 = y1;
+
+ MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+ // Stylus movement win HandwritingArea should trigger IMM.startHandwriting once.
+ verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
+ }
+
+ @Test
+ public void onTouchEvent_startHandwritingOnce_when_stylusMoveMultiTimes_withinHWArea() {
+ mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ final int x1 = (sHwArea.left + sHwArea.right) / 2;
+ final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
+ MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+ final int x2 = x1 + TOUCH_SLOP * 2;
+ final int y2 = y1;
+ MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+
+ final int x3 = x2 + TOUCH_SLOP * 2;
+ final int y3 = y2;
+ MotionEvent stylusEvent3 = createStylusEvent(ACTION_MOVE, x3, y3, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent3);
+
+ MotionEvent stylusEvent4 = createStylusEvent(ACTION_UP, x2, y2, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent4);
+
+ // It only calls startHandwriting once for each ACTION_DOWN.
+ verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
+ }
+
+ @Test
+ public void onTouchEvent_startHandwriting_inputConnectionBuiltAfterStylusMove() {
+ final int x1 = (sHwArea.left + sHwArea.right) / 2;
+ final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
+ MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+ final int x2 = x1 + TOUCH_SLOP * 2;
+ final int y2 = y1;
+ MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+ // InputConnection is created after stylus movement.
+ mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+
+ verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
+ }
+
+ @Test
+ public void onTouchEvent_notStartHandwriting_when_stylusTap_withinHWArea() {
+ mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ final int x1 = 200;
+ final int y1 = 200;
+ MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+ final int x2 = x1 + TOUCH_SLOP / 2;
+ final int y2 = y1;
+ MotionEvent stylusEvent2 = createStylusEvent(ACTION_UP, x2, y2, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+ verify(mHandwritingInitiator, never()).startHandwriting(mTestView);
+ }
+
+ @Test
+ public void onTouchEvent_notStartHandwriting_when_stylusMove_outOfHWArea() {
+ mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ final int x1 = 10;
+ final int y1 = 10;
+ MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+ final int x2 = x1 + TOUCH_SLOP * 2;
+ final int y2 = y1;
+ MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+ verify(mHandwritingInitiator, never()).startHandwriting(mTestView);
+ }
+
+ @Test
+ public void onTouchEvent_notStartHandwriting_when_stylusMove_afterTapTimeOut() {
+ mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ final int x1 = 10;
+ final int y1 = 10;
+ final long time1 = 10L;
+ MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+ final int x2 = x1 + TOUCH_SLOP * 2;
+ final int y2 = y1;
+ final long time2 = time1 + TAP_TIMEOUT + 10L;
+ MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, time2);
+ mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+ // stylus movement is after TAP_TIMEOUT it shouldn't call startHandwriting.
+ verify(mHandwritingInitiator, never()).startHandwriting(mTestView);
+ }
+
+ @Test
+ public void onInputConnectionCreated_inputConnectionCreated() {
+ mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ assertThat(mHandwritingInitiator.mConnectedView).isNotNull();
+ assertThat(mHandwritingInitiator.mConnectedView.get()).isEqualTo(mTestView);
+ }
+
+ @Test
+ public void onInputConnectionCreated_inputConnectionClosed() {
+ mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionClosed(mTestView);
+
+ assertThat(mHandwritingInitiator.mConnectedView).isNull();
+ assertThat(mHandwritingInitiator.mEditorBound).isNull();
+ }
+
+ @Test
+ public void onInputConnectionCreated_inputConnectionRestarted() {
+ // When IMM restarts input connection, View#onInputConnectionCreatedInternal might be
+ // called before View#onInputConnectionClosedInternal. As a result, we need to handle the
+ // case where "one view "2 InputConnections".
+ mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionClosed(mTestView);
+
+ assertThat(mHandwritingInitiator.mConnectedView).isNotNull();
+ assertThat(mHandwritingInitiator.mConnectedView.get()).isEqualTo(mTestView);
+ }
+
+ @Test
+ public void updateEditorBound() {
+ Rect rect = new Rect(1, 2, 3, 4);
+ mHandwritingInitiator.updateEditorBound(rect);
+
+ assertThat(mHandwritingInitiator.mEditorBound).isEqualTo(rect);
+ }
+
+ private MotionEvent createStylusEvent(int action, int x, int y, long eventTime) {
+ MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
+ properties[0].toolType = MotionEvent.TOOL_TYPE_STYLUS;
+
+ MotionEvent.PointerCoords[] coords = MotionEvent.PointerCoords.createArray(1);
+ coords[0].x = x;
+ coords[0].y = y;
+
+ return MotionEvent.obtain(0 /* downTime */, eventTime /* eventTime */, action, 1,
+ properties, coords, 0 /* metaState */, 0 /* buttonState */, 1 /* xPrecision */,
+ 1 /* yPrecision */, 0 /* deviceId */, 0 /* edgeFlags */,
+ InputDevice.SOURCE_TOUCHSCREEN, 0 /* flags */);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index c4c983d..78a8f7b 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -16,7 +16,6 @@
package android.view;
-import static android.view.InputDevice.SOURCE_CLASS_POINTER;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.TOOL_TYPE_FINGER;
@@ -215,27 +214,4 @@
rotInvalid.transform(mat);
assertEquals(-1, rotInvalid.getSurfaceRotation());
}
-
- @Test
- public void testUsesPointerSourceByDefault() {
- final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
- ACTION_DOWN, 0 /* x */, 0 /* y */, 0 /* metaState */);
- assertTrue(event.isFromSource(SOURCE_CLASS_POINTER));
- }
-
- @Test
- public void testLocationOffsetOnlyAppliedToNonPointerSources() {
- final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
- ACTION_DOWN, 10 /* x */, 20 /* y */, 0 /* metaState */);
- event.offsetLocation(40, 50);
-
- // The offset should be applied since a pointer source is used by default.
- assertEquals(50, (int) event.getX());
- assertEquals(70, (int) event.getY());
-
- // The offset should not be applied if the source is changed to a non-pointer source.
- event.setSource(InputDevice.SOURCE_JOYSTICK);
- assertEquals(10, (int) event.getX());
- assertEquals(20, (int) event.getY());
- }
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index e689b5d3..dd8cc6e 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -26,6 +26,8 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyObject;
@@ -997,6 +999,60 @@
}
}
+ @Test
+ public void enable_cacheEnabled() {
+ mAccessibilityCache.setEnabled(false);
+ assertFalse(mAccessibilityCache.isEnabled());
+
+ mAccessibilityCache.setEnabled(true);
+ assertTrue(mAccessibilityCache.isEnabled());
+ }
+
+ @Test
+ public void disable_cacheDisabled() {
+ mAccessibilityCache.setEnabled(false);
+ assertFalse(mAccessibilityCache.isEnabled());
+ }
+
+ @Test
+ public void queryNode_nodeIsInCache() {
+ AccessibilityNodeInfo info = new AccessibilityNodeInfo();
+ mAccessibilityCache.add(info);
+
+ assertTrue(mAccessibilityCache.isNodeInCache(info));
+ }
+
+ @Test
+ public void clearSubtreeWithNode_nodeInCacheInvalidated() {
+ AccessibilityNodeInfo info = new AccessibilityNodeInfo();
+ info.setSource(getMockViewWithA11yAndWindowIds(1, 1));
+ mAccessibilityCache.add(info);
+
+ mAccessibilityCache.clearSubTree(info);
+ assertFalse(mAccessibilityCache.isNodeInCache(info));
+ }
+
+ @Test
+ public void clearSubtreeWithNode_subtreeInCacheInvalidated() {
+ AccessibilityNodeInfo info = new AccessibilityNodeInfo();
+ View parentView = getMockViewWithA11yAndWindowIds(1, 1);
+ info.setSource(parentView);
+
+ AccessibilityNodeInfo childInfo = new AccessibilityNodeInfo();
+ View childView = getMockViewWithA11yAndWindowIds(2, 1);
+ childInfo.setSource(childView);
+
+ childInfo.setParent(parentView);
+ info.addChild(childView);
+ mAccessibilityCache.add(info);
+ mAccessibilityCache.add(childInfo);
+
+ mAccessibilityCache.clearSubTree(info);
+
+ assertFalse(mAccessibilityCache.isNodeInCache(info));
+ assertFalse(mAccessibilityCache.isNodeInCache(childInfo));
+ }
+
private AccessibilityWindowInfo obtainAccessibilityWindowInfo(int windowId, int layer) {
AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain();
windowInfo.setId(windowId);
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index 2c8c385..6df9002 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -42,14 +42,14 @@
// and assertAccessibilityEventCleared
/** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 34;
+ private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 32;
// The number of fields tested in the corresponding CTS AccessibilityRecordTest:
// assertAccessibilityRecordCleared, fullyPopulateAccessibilityRecord,
// and assertEqualAccessibilityRecord
/** The number of properties of the {@link AccessibilityRecord} class. */
- private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 25;
+ private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 23;
@Test
public void testImportantForAccessibiity_getSetWorkAcrossParceling() {
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 212fdca..bb1a3b18 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -17,7 +17,6 @@
package android.view.accessibility;
import static junit.framework.TestCase.assertFalse;
-import static junit.framework.TestCase.assertSame;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -188,17 +187,6 @@
}
@Test
- public void testSendAccessibilityEvent_AccessibilityEnabled() throws Exception {
- AccessibilityEvent sentEvent = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_ANNOUNCEMENT);
-
- AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
- manager.sendAccessibilityEvent(sentEvent);
-
- assertSame("The event should be recycled.", sentEvent, AccessibilityEvent.obtain());
- }
-
- @Test
public void testSendAccessibilityEvent_AccessibilityDisabled() throws Exception {
AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index ad1f298..62d0b2e 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -173,6 +173,8 @@
public void setFocusAppearance(int strokeWidth, int color) {}
+ public void setCacheEnabled(boolean enabled) {}
+
public void logTrace(long timestamp, String where, String callingParams, int processId,
long threadId, int callingUid, Bundle callingStack) {}
diff --git a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java
deleted file mode 100644
index 11f4e3c..0000000
--- a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * Copyright (C) 2009 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.view.accessibility;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * This class exercises the caching and recycling of {@link AccessibilityEvent}s.
- */
-public class RecycleAccessibilityEventTest extends TestCase {
-
- private static final String CLASS_NAME = "foo.bar.baz.Test";
- private static final String PACKAGE_NAME = "foo.bar.baz";
- private static final String TEXT = "Some stuff";
-
- private static final String CONTENT_DESCRIPTION = "Content description";
- private static final int ITEM_COUNT = 10;
- private static final int CURRENT_ITEM_INDEX = 1;
-
- private static final int FROM_INDEX = 1;
- private static final int ADDED_COUNT = 2;
- private static final int REMOVED_COUNT = 1;
-
- /**
- * If an {@link AccessibilityEvent} is marshaled/unmarshaled correctly
- */
- @SmallTest
- public void testAccessibilityEventViewTextChangedType() {
- AccessibilityEvent first =
- AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
- assertNotNull(first);
-
- first.setClassName(CLASS_NAME);
- first.setPackageName(PACKAGE_NAME);
- first.getText().add(TEXT);
- first.setFromIndex(FROM_INDEX);
- first.setAddedCount(ADDED_COUNT);
- first.setRemovedCount(REMOVED_COUNT);
- first.setChecked(true);
- first.setContentDescription(CONTENT_DESCRIPTION);
- first.setItemCount(ITEM_COUNT);
- first.setCurrentItemIndex(CURRENT_ITEM_INDEX);
- first.setEnabled(true);
- first.setPassword(true);
-
- first.recycle();
-
- assertNotNull(first);
- assertNull(first.getClassName());
- assertNull(first.getPackageName());
- assertEquals(0, first.getText().size());
- assertFalse(first.isChecked());
- assertNull(first.getContentDescription());
- assertEquals(-1, first.getItemCount());
- assertEquals(AccessibilityEvent.INVALID_POSITION, first.getCurrentItemIndex());
- assertFalse(first.isEnabled());
- assertFalse(first.isPassword());
- assertEquals(-1, first.getFromIndex());
- assertEquals(-1, first.getAddedCount());
- assertEquals(-1, first.getRemovedCount());
-
- // get another event from the pool (this must be the recycled first)
- AccessibilityEvent second = AccessibilityEvent.obtain();
- assertEquals(first, second);
- }
-}
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index 9696fdf..4f95cb8 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -28,7 +28,7 @@
import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
-import static com.android.internal.widget.FloatingToolbarPopup.MenuItemRepr;
+import static com.android.internal.widget.floatingtoolbar.LocalFloatingToolbarPopup.MenuItemRepr;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
@@ -42,7 +42,7 @@
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.ViewInteraction;
-import com.android.internal.widget.FloatingToolbar;
+import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index c0ced6c..69ff7c6 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -180,6 +180,16 @@
return clientIntent;
}
+ /**
+ * Whether {@code #testIsAppPredictionServiceAvailable} should verify the behavior after
+ * changing the availability conditions at runtime. In the unbundled chooser, the availability
+ * is cached at start and will never be re-evaluated.
+ * TODO: remove when we no longer want to test the system's on-the-fly evaluation.
+ */
+ protected boolean shouldTestTogglingAppPredictionServiceAvailabilityAtRuntime() {
+ return true;
+ }
+
/* --------
* The code in this section is unorthodox and can be simplified/reverted when we no longer need
* to support the parallel chooser implementations.
@@ -784,7 +794,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -802,7 +813,7 @@
assertThat(logger.event(4).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
- // SHARESHEET_EDIT_TARGET_SELECTED:
+ // SHARESHEET_NEARBY_TARGET_SELECTED:
assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
assertThat(logger.get(5).targetType,
is(ChooserActivityLogger
@@ -814,7 +825,7 @@
- @Test
+ @Test @Ignore
public void testEditImageLogs() throws Exception {
Intent sendIntent = createSendImageIntent(
Uri.parse("android.resource://com.android.frameworks.coretests/"
@@ -853,7 +864,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("image/png"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -1321,6 +1333,10 @@
} else {
assertThat(activity.isAppPredictionServiceAvailable(), is(true));
+ if (!shouldTestTogglingAppPredictionServiceAvailabilityAtRuntime()) {
+ return;
+ }
+
ChooserActivityOverrideData.getInstance().resources =
Mockito.spy(activity.getResources());
when(
@@ -2101,7 +2117,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -2119,7 +2136,7 @@
assertThat(logger.event(4).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
- // SHARESHEET_EDIT_TARGET_SELECTED:
+ // SHARESHEET_APP_TARGET_SELECTED:
assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
assertThat(logger.get(5).targetType,
is(ChooserActivityLogger
@@ -2197,7 +2214,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -2215,7 +2233,7 @@
.SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()));
}
- @Test
+ @Test @Ignore
public void testEmptyDirectRowLogging() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
@@ -2259,7 +2277,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -2320,7 +2339,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -2338,7 +2358,7 @@
assertThat(logger.event(4).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
- // SHARESHEET_EDIT_TARGET_SELECTED:
+ // SHARESHEET_COPY_TARGET_SELECTED:
assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
assertThat(logger.get(5).targetType,
is(ChooserActivityLogger
@@ -2386,7 +2406,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is(TEST_MIME_TYPE));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
index aea453e..caec365 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
@@ -24,6 +24,7 @@
import android.os.FileUtils;
import android.os.SystemProperties;
+import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -46,6 +47,7 @@
import java.io.InputStream;
import java.util.ArrayList;
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class OverlayConfigTest {
private static final String TEST_APK_PACKAGE_NAME =
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index c1a45c4..388cf6e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -22,6 +22,8 @@
import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -36,6 +38,7 @@
import android.app.ActivityManager;
import android.os.BatteryStats;
+import android.os.WakeLockStats;
import android.util.SparseArray;
import android.view.Display;
@@ -50,6 +53,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
@LargeTest
@RunWith(AndroidJUnit4.class)
@SuppressWarnings("GuardedBy")
@@ -507,4 +512,51 @@
final BatteryStatsImpl.Uid u = mBatteryStatsImpl.getUidStatsLocked(parentUid);
u.addIsolatedUid(childUid);
}
+
+ @Test
+ public void testGetWakeLockStats() {
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+ // First wakelock, acquired once, not currently held
+ mMockClock.realtime = 1000;
+ mBatteryStatsImpl.noteStartWakeLocked(10100, 100, null, "wakeLock1", null,
+ BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+ mMockClock.realtime = 3000;
+ mBatteryStatsImpl.noteStopWakeLocked(10100, 100, null, "wakeLock1", null,
+ BatteryStats.WAKE_TYPE_PARTIAL);
+
+ // Second wakelock, acquired twice, still held
+ mMockClock.realtime = 4000;
+ mBatteryStatsImpl.noteStartWakeLocked(10200, 101, null, "wakeLock2", null,
+ BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+ mMockClock.realtime = 5000;
+ mBatteryStatsImpl.noteStopWakeLocked(10200, 101, null, "wakeLock2", null,
+ BatteryStats.WAKE_TYPE_PARTIAL);
+
+ mMockClock.realtime = 6000;
+ mBatteryStatsImpl.noteStartWakeLocked(10200, 101, null, "wakeLock2", null,
+ BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+ mMockClock.realtime = 9000;
+
+ List<WakeLockStats.WakeLock> wakeLockStats =
+ mBatteryStatsImpl.getWakeLockStats().getWakeLocks();
+ assertThat(wakeLockStats).hasSize(2);
+
+ WakeLockStats.WakeLock wakeLock1 = wakeLockStats.stream()
+ .filter(wl -> wl.uid == 10100 && wl.name.equals("wakeLock1")).findFirst().get();
+
+ assertThat(wakeLock1.timesAcquired).isEqualTo(1);
+ assertThat(wakeLock1.timeHeldMs).isEqualTo(0); // Not currently held
+ assertThat(wakeLock1.totalTimeHeldMs).isEqualTo(2000); // 3000-1000
+
+ WakeLockStats.WakeLock wakeLock2 = wakeLockStats.stream()
+ .filter(wl -> wl.uid == 10200 && wl.name.equals("wakeLock2")).findFirst().get();
+
+ assertThat(wakeLock2.timesAcquired).isEqualTo(2);
+ assertThat(wakeLock2.timeHeldMs).isEqualTo(3000); // 9000-6000
+ assertThat(wakeLock2.totalTimeHeldMs).isEqualTo(4000); // (5000-4000) + (9000-6000)
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index 7db31fb..9b3876f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -39,6 +39,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import libcore.testing.io.TestIoUtils;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,9 +54,12 @@
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
private static final long MINUTE_IN_MS = 60 * 1000;
+ private final File mHistoryDir =
+ TestIoUtils.createTemporaryDirectory(getClass().getSimpleName());
@Rule
- public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345)
- .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0);
+ public final BatteryUsageStatsRule mStatsRule =
+ new BatteryUsageStatsRule(12345, mHistoryDir)
+ .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0);
@Test
public void test_getBatteryUsageStats() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index f75a6df..b3056e2 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -40,6 +40,7 @@
import org.junit.runners.model.Statement;
import org.mockito.stubbing.Answer;
+import java.io.File;
import java.util.Arrays;
public class BatteryUsageStatsRule implements TestRule {
@@ -57,14 +58,18 @@
private boolean mScreenOn;
public BatteryUsageStatsRule() {
- this(0);
+ this(0, null);
}
public BatteryUsageStatsRule(long currentTime) {
+ this(currentTime, null);
+ }
+
+ public BatteryUsageStatsRule(long currentTime, File historyDir) {
Context context = InstrumentationRegistry.getContext();
mPowerProfile = spy(new PowerProfile(context, true /* forTest */));
mMockClock.currentTime = currentTime;
- mBatteryStats = new MockBatteryStatsImpl(mMockClock);
+ mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir);
mBatteryStats.setPowerProfile(mPowerProfile);
mBatteryStats.onSystemReady();
}
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
index ad4c3fe..b8a4685 100644
--- a/graphics/java/android/graphics/RenderEffect.java
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -290,6 +290,22 @@
return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance()));
}
+ /**
+ * Create a {@link RenderEffect} that executes the provided {@link RuntimeShader} and passes
+ * the contents of the {@link android.graphics.RenderNode} that this RenderEffect is installed
+ * on as an input to the shader.
+ * @param shader the runtime shader that will bind the inputShaderName to the RenderEffect input
+ * @param uniformShaderName the uniform name defined in the RuntimeShader's program to which
+ * the contents of the RenderNode will be bound
+ */
+ @NonNull
+ public static RenderEffect createRuntimeShaderEffect(
+ @NonNull RuntimeShader shader, @NonNull String uniformShaderName) {
+ return new RenderEffect(
+ nativeCreateRuntimeShaderEffect(shader.getNativeShaderBuilder(),
+ uniformShaderName));
+ }
+
private final long mNativeRenderEffect;
/* only constructed from static factory methods */
@@ -318,5 +334,7 @@
private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode);
private static native long nativeCreateChainEffect(long outer, long inner);
private static native long nativeCreateShaderEffect(long shader);
+ private static native long nativeCreateRuntimeShaderEffect(
+ long shaderBuilder, String inputShaderName);
private static native long nativeGetFinalizer();
}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index cdff585..e9b3c49 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -43,7 +43,7 @@
name: "wm_shell_util-sources",
srcs: [
"src/com/android/wm/shell/util/**/*.java",
- "src/com/android/wm/shell/common/split/SplitScreenConstants.java"
+ "src/com/android/wm/shell/common/split/SplitScreenConstants.java",
],
path: "src",
}
@@ -74,13 +74,13 @@
],
tools: ["protologtool"],
cmd: "$(location protologtool) transform-protolog-calls " +
- "--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " +
- "--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " +
- "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
- "--loggroups-jar $(location :wm_shell_protolog-groups) " +
- "--output-srcjar $(out) " +
- "$(locations :wm_shell-sources)",
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " +
+ "--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " +
+ "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+ "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+ "--output-srcjar $(out) " +
+ "$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.srcjar"],
}
@@ -92,13 +92,14 @@
],
tools: ["protologtool"],
cmd: "$(location protologtool) generate-viewer-config " +
- "--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
- "--loggroups-jar $(location :wm_shell_protolog-groups) " +
- "--viewer-conf $(out) " +
- "$(locations :wm_shell-sources)",
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+ "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+ "--viewer-conf $(out) " +
+ "$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.json"],
}
+
// End ProtoLog
java_library {
@@ -123,11 +124,12 @@
"res",
],
java_resources: [
- ":generate-wm_shell_protolog.json"
+ ":generate-wm_shell_protolog.json",
],
static_libs: [
"androidx.appcompat_appcompat",
"androidx.arch.core_core-runtime",
+ "androidx-constraintlayout_constraintlayout",
"androidx.dynamicanimation_dynamicanimation",
"androidx.recyclerview_recyclerview",
"kotlinx-coroutines-android",
diff --git a/libs/WindowManager/Shell/res/layout/badged_image_view.xml b/libs/WindowManager/Shell/res/layout/badged_image_view.xml
new file mode 100644
index 0000000..5f07121
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/badged_image_view.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <ImageView
+ android:id="@+id/icon_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:contentDescription="@null" />
+
+ <!--
+ Icon badge size is defined in Launcher3 BaseIconFactory as 0.444 of icon size.
+ Constraint guide starts from left, which means for a badge positioned on the right,
+ percent has to be 1 - 0.444 to have the same effect.
+ -->
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/app_icon_constraint_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.556" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/app_icon_constraint_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_percent="0.556" />
+
+ <ImageView
+ android:id="@+id/app_icon_view"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:contentDescription="@null"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="@id/app_icon_constraint_vertical"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="@id/app_icon_constraint_horizontal" />
+
+</merge>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index 686fbbf..c52d87d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Path;
@@ -27,14 +26,16 @@
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.PathParser;
-import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewOutlineProvider;
-import android.widget.FrameLayout;
import android.widget.ImageView;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
+import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import java.util.EnumSet;
@@ -46,14 +47,12 @@
* Badge = the icon associated with the app that created this bubble, this will show work profile
* badge if appropriate.
*/
-public class BadgedImageView extends FrameLayout {
+public class BadgedImageView extends ConstraintLayout {
/** Same value as Launcher3 dot code */
public static final float WHITE_SCRIM_ALPHA = 0.54f;
/** Same as value in Launcher3 IconShape */
public static final int DEFAULT_PATH_SIZE = 100;
- /** Same as value in Launcher3 BaseIconFactory */
- private static final float ICON_BADGE_SCALE = 0.444f;
/**
* Flags that suppress the visibility of the 'new' dot, for one reason or another. If any of
@@ -105,11 +104,13 @@
public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ // We manage positioning the badge ourselves
+ setLayoutDirection(LAYOUT_DIRECTION_LTR);
- mBubbleIcon = new ImageView(context);
- addView(mBubbleIcon);
- mAppIcon = new ImageView(context);
- addView(mAppIcon);
+ LayoutInflater.from(context).inflate(R.layout.badged_image_view, this);
+
+ mBubbleIcon = findViewById(R.id.icon_view);
+ mAppIcon = findViewById(R.id.app_icon_view);
final TypedArray ta = mContext.obtainStyledAttributes(attrs, new int[]{android.R.attr.src},
defStyleAttr, defStyleRes);
@@ -161,6 +162,7 @@
public void setRenderedBubble(BubbleViewProvider bubble) {
mBubble = bubble;
mBubbleIcon.setImageBitmap(bubble.getBubbleIcon());
+ mAppIcon.setImageBitmap(bubble.getAppBadge());
if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) {
hideBadge();
} else {
@@ -348,26 +350,17 @@
}
void showBadge() {
- Bitmap badge = mBubble.getAppBadge();
- if (badge == null) {
+ if (mBubble.getAppBadge() == null) {
mAppIcon.setVisibility(GONE);
return;
}
-
- final int bubbleSize = mBubble.getBubbleIcon().getWidth();
- final int badgeSize = (int) (ICON_BADGE_SCALE * bubbleSize);
-
- FrameLayout.LayoutParams appIconParams = (LayoutParams) mAppIcon.getLayoutParams();
- appIconParams.height = badgeSize;
- appIconParams.width = badgeSize;
+ int translationX;
if (mOnLeft) {
- appIconParams.gravity = Gravity.BOTTOM | Gravity.LEFT;
+ translationX = -(mBubbleIcon.getWidth() - mAppIcon.getWidth());
} else {
- appIconParams.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+ translationX = 0;
}
- mAppIcon.setLayoutParams(appIconParams);
-
- mAppIcon.setImageBitmap(badge);
+ mAppIcon.setTranslationX(translationX);
mAppIcon.setVisibility(VISIBLE);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index af59062..9ae67a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -386,13 +386,14 @@
final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
android.R.attr.dialogCornerRadius,
android.R.attr.colorBackgroundFloating});
- mCornerRadius = ta.getDimensionPixelSize(0, 0);
+ boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+ mContext.getResources());
+ mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
mBackgroundColorFloating = ta.getColor(1, Color.WHITE);
mExpandedViewContainer.setBackgroundColor(mBackgroundColorFloating);
ta.recycle();
- if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
- mContext.getResources())) {
+ if (mTaskView != null) {
mTaskView.setCornerRadius(mCornerRadius);
}
updatePointerView();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index b40021e..79b7653 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -70,6 +70,7 @@
import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
@@ -822,7 +823,9 @@
mAnimatingOutSurfaceView = new SurfaceView(getContext());
mAnimatingOutSurfaceView.setUseAlpha();
mAnimatingOutSurfaceView.setZOrderOnTop(true);
- mAnimatingOutSurfaceView.setCornerRadius(mCornerRadius);
+ boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+ mContext.getResources());
+ mAnimatingOutSurfaceView.setCornerRadius(supportsRoundedCorners ? mCornerRadius : 0);
mAnimatingOutSurfaceView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
mAnimatingOutSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 507204c..c9c73fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -277,6 +277,7 @@
registerSettingObservers(mUserId);
setupTimeoutListener();
updateSettings();
+ updateDisplayLayout(mContext.getDisplayId());
mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
@@ -448,8 +449,13 @@
onShortcutEnabledChanged();
}
- private void updateDisplayLayout(int displayId) {
+ @VisibleForTesting
+ void updateDisplayLayout(int displayId) {
final DisplayLayout newDisplayLayout = mDisplayController.getDisplayLayout(displayId);
+ if (newDisplayLayout == null) {
+ Slog.w(TAG, "Failed to get new DisplayLayout.");
+ return;
+ }
mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
mTutorialHandler.onDisplayChanged(newDisplayLayout);
mBackgroundPanelOrganizer.onDisplayChanged(newDisplayLayout);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 1b2f476..ec3ef5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -123,9 +123,8 @@
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
ShellExecutor mainExecutor) {
super(mainExecutor);
- mDisplayLayout.set(displayLayout);
+ setDisplayLayout(displayLayout);
mOneHandedSettingsUtil = oneHandedSettingsUtil;
- updateDisplayBounds();
mAnimationController = animationController;
final int animationDurationConfig = context.getResources().getInteger(
R.integer.config_one_handed_translate_animation_duration);
@@ -282,6 +281,7 @@
@VisibleForTesting
void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
mDisplayLayout.set(displayLayout);
+ updateDisplayBounds();
}
@VisibleForTesting
@@ -289,6 +289,7 @@
return mDisplayAreaTokenMap;
}
+ @VisibleForTesting
void updateDisplayBounds() {
mDefaultDisplayBounds.set(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
mLastVisualDisplayBounds.set(mDefaultDisplayBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 9575b0a..e616172 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -584,9 +584,11 @@
setCurrentValue(bounds);
if (inScaleTransition() || sourceHintRect == null) {
if (isOutPipDirection) {
- getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
+ getSurfaceTransactionHelper().crop(tx, leash, end)
+ .scale(tx, leash, end, bounds);
} else {
- getSurfaceTransactionHelper().scale(tx, leash, base, bounds, angle)
+ getSurfaceTransactionHelper().crop(tx, leash, base)
+ .scale(tx, leash, base, bounds, angle)
.round(tx, leash, base, bounds);
}
} else {
@@ -622,13 +624,13 @@
if (rotationDelta == ROTATION_90) {
degree = 90 * (1 - fraction);
x = fraction * (end.left - start.left)
- + start.left + start.right * (1 - fraction);
+ + start.left + start.width() * (1 - fraction);
y = fraction * (end.top - start.top) + start.top;
} else {
degree = -90 * (1 - fraction);
x = fraction * (end.left - start.left) + start.left;
y = fraction * (end.top - start.top)
- + start.top + start.bottom * (1 - fraction);
+ + start.top + start.height() * (1 - fraction);
}
} else {
if (rotationDelta == ROTATION_90) {
@@ -646,8 +648,10 @@
getSurfaceTransactionHelper()
.rotateAndScaleWithCrop(tx, leash, initialContainerRect, bounds,
insets, degree, x, y, isOutPipDirection,
- rotationDelta == ROTATION_270 /* clockwise */)
- .round(tx, leash, sourceBounds, bounds);
+ rotationDelta == ROTATION_270 /* clockwise */);
+ if (shouldApplyCornerRadius()) {
+ getSurfaceTransactionHelper().round(tx, leash, sourceBounds, bounds);
+ }
tx.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index a8d4d1c..4c09a4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -416,17 +416,32 @@
final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
- final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
- mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds());
- tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
- // We set to fullscreen here for now, but later it will be set to UNDEFINED for
- // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
- wct.setActivityWindowingMode(mToken,
- direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit
- ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- : WINDOWING_MODE_FULLSCREEN);
- wct.setBounds(mToken, destinationBounds);
- wct.setBoundsChangeTransaction(mToken, tx);
+
+ if (Transitions.ENABLE_SHELL_TRANSITIONS && direction == TRANSITION_DIRECTION_LEAVE_PIP) {
+ // When exit to fullscreen with Shell transition enabled, we update the Task windowing
+ // mode directly so that it can also trigger display rotation and visibility update in
+ // the same transition if there will be any.
+ wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ // We can inherit the parent bounds as it is going to be fullscreen. The
+ // destinationBounds calculated above will be incorrect if this is with rotation.
+ wct.setBounds(mToken, null);
+ } else {
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
+ mPipBoundsState.getBounds());
+ tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
+ // We set to fullscreen here for now, but later it will be set to UNDEFINED for
+ // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
+ wct.setActivityWindowingMode(mToken,
+ direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+ && !requestEnterSplit
+ ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ : WINDOWING_MODE_FULLSCREEN);
+ wct.setBounds(mToken, destinationBounds);
+ wct.setBoundsChangeTransaction(mToken, tx);
+ }
+
// Set the exiting state first so if there is fixed rotation later, the running animation
// won't be interrupted by alpha animation for existing PiP.
mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
@@ -729,24 +744,18 @@
if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
return;
}
+ if (Transitions.ENABLE_SHELL_TRANSITIONS
+ && mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP) {
+ // With Shell transition, we do the cleanup in PipTransition after exiting PIP.
+ return;
+ }
final WindowContainerToken token = info.token;
Objects.requireNonNull(token, "Requires valid WindowContainerToken");
if (token.asBinder() != mToken.asBinder()) {
Log.wtf(TAG, "Unrecognized token: " + token);
return;
}
- clearWaitForFixedRotation();
- mPipTransitionState.setInSwipePipToHomeTransition(false);
- mPictureInPictureParams = null;
- mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
- // Re-set the PIP bounds to none.
- mPipBoundsState.setBounds(new Rect());
- mPipUiEventLoggerLogger.setTaskInfo(null);
- mPipMenuController.detach();
-
- if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
- mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
- }
+ onExitPipFinished(info);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mPipTransitionController.forceFinishTransition();
@@ -843,6 +852,22 @@
clearWaitForFixedRotation();
}
+ /** Called when exiting PIP tranisiton is finished to do the state cleanup. */
+ void onExitPipFinished(TaskInfo info) {
+ clearWaitForFixedRotation();
+ mPipTransitionState.setInSwipePipToHomeTransition(false);
+ mPictureInPictureParams = null;
+ mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
+ // Re-set the PIP bounds to none.
+ mPipBoundsState.setBounds(new Rect());
+ mPipUiEventLoggerLogger.setTaskInfo(null);
+ mPipMenuController.detach();
+
+ if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
+ mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
+ }
+ }
+
private void fadeExistingPip(boolean show) {
final float alphaStart = show ? 0 : 1;
final float alphaEnd = show ? 1 : 0;
@@ -1435,7 +1460,7 @@
/**
* Fades out and removes an overlay surface.
*/
- private void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback,
+ void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback,
boolean withStartDelay) {
if (surface == null) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index e440feb..c6794b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -19,10 +19,16 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.util.RotationUtils.deltaRotation;
+import static android.util.RotationUtils.rotateBounds;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PIP;
+import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.isIndependent;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
@@ -30,6 +36,7 @@
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -38,13 +45,15 @@
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
-import android.util.Log;
+import android.util.ArrayMap;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -54,6 +63,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.CounterRotator;
import java.util.Optional;
@@ -65,14 +75,19 @@
private static final String TAG = PipTransition.class.getSimpleName();
+ private final Context mContext;
private final PipTransitionState mPipTransitionState;
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<SplitScreenController> mSplitScreenOptional;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
- private Rect mExitDestinationBounds = new Rect();
- private IBinder mExitTransition = null;
+ private final Rect mExitDestinationBounds = new Rect();
+ @Nullable
+ private IBinder mExitTransition;
+ /** The Task window that is currently in PIP windowing mode. */
+ @Nullable
+ private WindowContainerToken mCurrentPipTaskToken;
public PipTransition(Context context,
PipBoundsState pipBoundsState,
@@ -86,6 +101,7 @@
Optional<SplitScreenController> splitScreenOptional) {
super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
pipAnimationController, transitions, shellTaskOrganizer);
+ mContext = context;
mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
@@ -117,112 +133,57 @@
}
@Override
- public boolean startAnimation(@android.annotation.NonNull IBinder transition,
- @android.annotation.NonNull TransitionInfo info,
- @android.annotation.NonNull SurfaceControl.Transaction startTransaction,
- @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
- @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ // Exiting PIP.
final int type = info.getType();
- if (mExitTransition == transition) {
+ if (transition.equals(mExitTransition)) {
+ mExitDestinationBounds.setEmpty();
mExitTransition = null;
- if (type == TRANSIT_EXIT_PIP_TO_SPLIT) {
- return startExitToSplitAnimation(
- info, startTransaction, finishTransaction, finishCallback);
- }
-
- if (info.getChanges().size() == 1) {
- if (mFinishCallback != null) {
- mFinishCallback.onTransitionFinished(null, null);
- mFinishCallback = null;
- throw new RuntimeException("Previous callback not called, aborting exit PIP.");
- }
-
- final TransitionInfo.Change change = info.getChanges().get(0);
- mFinishCallback = finishCallback;
- startTransaction.apply();
- boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(),
- new Rect(mExitDestinationBounds));
- mExitDestinationBounds.setEmpty();
- return success;
- } else {
- Log.e(TAG, "Got an exit-pip transition with unexpected change-list");
- }
- }
-
- if (type == TRANSIT_REMOVE_PIP) {
if (mFinishCallback != null) {
- mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
+ mFinishCallback.onTransitionFinished(null, null);
mFinishCallback = null;
- throw new RuntimeException("Previous callback not called, aborting remove PIP.");
+ throw new RuntimeException("Previous callback not called, aborting exit PIP.");
}
- startTransaction.apply();
- finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
- mPipBoundsState.getDisplayBounds());
- finishCallback.onTransitionFinished(null, null);
+ final TransitionInfo.Change exitPipChange = findCurrentPipChange(info);
+ if (exitPipChange == null) {
+ throw new RuntimeException("Cannot find the pip window for exit-pip transition.");
+ }
+
+ switch (type) {
+ case TRANSIT_EXIT_PIP:
+ startExitAnimation(info, startTransaction, finishCallback, exitPipChange);
+ break;
+ case TRANSIT_EXIT_PIP_TO_SPLIT:
+ startExitToSplitAnimation(info, startTransaction, finishTransaction,
+ finishCallback, exitPipChange);
+ break;
+ case TRANSIT_REMOVE_PIP:
+ removePipImmediately(info, startTransaction, finishTransaction, finishCallback,
+ exitPipChange);
+ break;
+ default:
+ throw new IllegalStateException("mExitTransition with unexpected transit type="
+ + transitTypeToString(type));
+ }
+ mCurrentPipTaskToken = null;
return true;
}
- // We only support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
- // that enter PiP instantly on opening, mostly from CTS/Flicker tests)
- if (type != TRANSIT_PIP && type != TRANSIT_OPEN) {
- // In case the PIP window is part of rotation transition, reset the bounds and rounded
- // corner.
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getMode() == TRANSIT_CHANGE && change.getTaskInfo() != null
- && change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_PINNED) {
- final SurfaceControl leash = change.getLeash();
- final Rect destBounds = mPipBoundsState.getBounds();
- final boolean isInPip = mPipTransitionState.isInPip();
- mSurfaceTransactionHelper
- .crop(startTransaction, leash, destBounds)
- .round(startTransaction, leash, isInPip);
- mSurfaceTransactionHelper
- .crop(finishTransaction, leash, destBounds)
- .round(finishTransaction, leash, isInPip);
- break;
- }
- }
- return false;
+ // Entering PIP.
+ if (isEnteringPip(info, mCurrentPipTaskToken)) {
+ return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
}
- // Search for an Enter PiP transition (along with a show wallpaper one)
- TransitionInfo.Change enterPip = null;
- TransitionInfo.Change wallpaper = null;
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getTaskInfo() != null
- && change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_PINNED) {
- enterPip = change;
- } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
- wallpaper = change;
- }
- }
- if (enterPip == null) {
- return false;
- }
-
- if (mFinishCallback != null) {
- mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
- mFinishCallback = null;
- throw new RuntimeException("Previous callback not called, aborting entering PIP.");
- }
-
- // Show the wallpaper if there is a wallpaper change.
- if (wallpaper != null) {
- startTransaction.show(wallpaper.getLeash());
- startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
- }
-
- mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
- mFinishCallback = finishCallback;
- return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
- startTransaction, finishTransaction, enterPip.getStartRotation(),
- enterPip.getEndRotation());
+ // For transition that we don't animate, we may need to update the PIP surface, otherwise it
+ // will be reset after the transition.
+ updatePipForUnhandledTransition(info, startTransaction, finishTransaction);
+ return false;
}
@Nullable
@@ -264,6 +225,7 @@
new Rect(mExitDestinationBounds));
}
mExitDestinationBounds.setEmpty();
+ mCurrentPipTaskToken = null;
}
@Override
@@ -296,7 +258,142 @@
mFinishCallback = null;
}
- private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
+ @Nullable
+ private TransitionInfo.Change findCurrentPipChange(@NonNull TransitionInfo info) {
+ if (mCurrentPipTaskToken == null) {
+ return null;
+ }
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (mCurrentPipTaskToken.equals(change.getContainer())) {
+ return change;
+ }
+ }
+ return null;
+ }
+
+ private void startExitAnimation(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull TransitionInfo.Change pipChange) {
+ TransitionInfo.Change displayRotationChange = null;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getMode() == TRANSIT_CHANGE
+ && (change.getFlags() & FLAG_IS_DISPLAY) != 0
+ && change.getStartRotation() != change.getEndRotation()) {
+ displayRotationChange = change;
+ break;
+ }
+ }
+
+ if (displayRotationChange != null) {
+ // Exiting PIP to fullscreen with orientation change.
+ startExpandAndRotationAnimation(info, startTransaction, finishCallback,
+ displayRotationChange, pipChange);
+ return;
+ }
+
+ // When there is no rotation, we can simply expand the PIP window.
+ mFinishCallback = (wct, wctCB) -> {
+ mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+ finishCallback.onTransitionFinished(wct, wctCB);
+ };
+
+ // Set the initial frame as scaling the end to the start.
+ final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds());
+ final Point offset = pipChange.getEndRelOffset();
+ destinationBounds.offset(-offset.x, -offset.y);
+ startTransaction.setWindowCrop(pipChange.getLeash(), destinationBounds);
+ mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(),
+ destinationBounds, mPipBoundsState.getBounds());
+ startTransaction.apply();
+ startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds);
+ }
+
+ private void startExpandAndRotationAnimation(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull TransitionInfo.Change displayRotationChange,
+ @NonNull TransitionInfo.Change pipChange) {
+ final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(),
+ displayRotationChange.getEndRotation());
+ final int displayW = displayRotationChange.getEndAbsBounds().width();
+ final int displayH = displayRotationChange.getEndAbsBounds().height();
+
+ // Counter-rotate all "going-away" things since they are still in the old orientation.
+ final ArrayMap<WindowContainerToken, CounterRotator> counterRotators = new ArrayMap<>();
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (!Transitions.isClosingType(change.getMode())
+ || !isIndependent(change, info)
+ || change.getParent() == null) {
+ continue;
+ }
+ CounterRotator crot = counterRotators.get(change.getParent());
+ if (crot == null) {
+ crot = new CounterRotator();
+ crot.setup(startTransaction,
+ info.getChange(change.getParent()).getLeash(),
+ rotateDelta, displayW, displayH);
+ if (crot.getSurface() != null) {
+ // Wallpaper should be placed at the bottom.
+ final int layer = (change.getFlags() & FLAG_IS_WALLPAPER) == 0
+ ? info.getChanges().size() - i
+ : -1;
+ startTransaction.setLayer(crot.getSurface(), layer);
+ }
+ counterRotators.put(change.getParent(), crot);
+ }
+ crot.addChild(startTransaction, change.getLeash());
+ }
+ mFinishCallback = (wct, wctCB) -> {
+ for (int i = 0; i < counterRotators.size(); ++i) {
+ counterRotators.valueAt(i).cleanUp(info.getRootLeash());
+ }
+ mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+ finishCallback.onTransitionFinished(wct, wctCB);
+ };
+
+ // Get the start bounds in new orientation.
+ final Rect startBounds = new Rect(pipChange.getStartAbsBounds());
+ rotateBounds(startBounds, displayRotationChange.getStartAbsBounds(), rotateDelta);
+ final Rect endBounds = new Rect(pipChange.getEndAbsBounds());
+ final Point offset = pipChange.getEndRelOffset();
+ startBounds.offset(-offset.x, -offset.y);
+ endBounds.offset(-offset.x, -offset.y);
+
+ // Reverse the rotation direction for expansion.
+ final int pipRotateDelta = deltaRotation(rotateDelta, 0);
+
+ // Set the start frame.
+ final int degree, x, y;
+ if (pipRotateDelta == ROTATION_90) {
+ degree = 90;
+ x = startBounds.right;
+ y = startBounds.top;
+ } else {
+ degree = -90;
+ x = startBounds.left;
+ y = startBounds.bottom;
+ }
+ mSurfaceTransactionHelper.rotateAndScaleWithCrop(startTransaction, pipChange.getLeash(),
+ endBounds, startBounds, new Rect(), degree, x, y, true /* isExpanding */,
+ pipRotateDelta == ROTATION_270 /* clockwise */);
+ startTransaction.apply();
+
+ // Expand and rotate the pip window to fullscreen.
+ final PipAnimationController.PipTransitionAnimator animator =
+ mPipAnimationController.getAnimator(pipChange.getTaskInfo(), pipChange.getLeash(),
+ startBounds, startBounds, endBounds, null, TRANSITION_DIRECTION_LEAVE_PIP,
+ 0 /* startingAngle */, pipRotateDelta);
+ animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(mEnterExitAnimationDuration)
+ .start();
+ }
+
+ private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
final Rect destinationBounds) {
PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(),
@@ -307,8 +404,87 @@
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
.start();
+ }
- return true;
+ /** For {@link Transitions#TRANSIT_REMOVE_PIP}, we just immediately remove the PIP Task. */
+ private void removePipImmediately(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull TransitionInfo.Change pipChange) {
+ startTransaction.apply();
+ finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
+ mPipBoundsState.getDisplayBounds());
+ mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+ finishCallback.onTransitionFinished(null, null);
+ }
+
+ /** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */
+ private static boolean isEnteringPip(@NonNull TransitionInfo info,
+ @Nullable WindowContainerToken currentPipTaskToken) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED
+ && !change.getContainer().equals(currentPipTaskToken)) {
+ // We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
+ // that enter PiP instantly on opening, mostly from CTS/Flicker tests)
+ if (info.getType() == TRANSIT_PIP || info.getType() == TRANSIT_OPEN) {
+ return true;
+ }
+ // This can happen if the request to enter PIP happens when we are collecting for
+ // another transition, such as TRANSIT_CHANGE (display rotation).
+ if (info.getType() == TRANSIT_CHANGE) {
+ return true;
+ }
+
+ // Please file a bug to handle the unexpected transition type.
+ throw new IllegalStateException("Entering PIP with unexpected transition type="
+ + transitTypeToString(info.getType()));
+ }
+ }
+ return false;
+ }
+
+ private boolean startEnterAnimation(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ // Search for an Enter PiP transition (along with a show wallpaper one)
+ TransitionInfo.Change enterPip = null;
+ TransitionInfo.Change wallpaper = null;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
+ enterPip = change;
+ } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+ wallpaper = change;
+ }
+ }
+ if (enterPip == null) {
+ return false;
+ }
+ // Keep track of the PIP task.
+ mCurrentPipTaskToken = enterPip.getContainer();
+
+ if (mFinishCallback != null) {
+ mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
+ mFinishCallback = null;
+ throw new RuntimeException("Previous callback not called, aborting entering PIP.");
+ }
+
+ // Show the wallpaper if there is a wallpaper change.
+ if (wallpaper != null) {
+ startTransaction.show(wallpaper.getLeash());
+ startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
+ }
+
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
+ mFinishCallback = finishCallback;
+ return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
+ startTransaction, finishTransaction, enterPip.getStartRotation(),
+ enterPip.getEndRotation());
}
private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
@@ -359,6 +535,11 @@
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
0 /* startingAngle */, rotationDelta);
+ if (sourceHintRect == null) {
+ // We use content overlay when there is no source rect hint to enter PiP use bounds
+ // animation.
+ animator.setUseContentOverlay(mContext);
+ }
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
startTransaction.setAlpha(leash, 0f);
// PiP menu is attached late in the process here to avoid any artifacts on the leash
@@ -380,10 +561,11 @@
return true;
}
- private boolean startExitToSplitAnimation(TransitionInfo info,
+ private void startExitToSplitAnimation(TransitionInfo info,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
- Transitions.TransitionFinishCallback finishCallback) {
+ Transitions.TransitionFinishCallback finishCallback,
+ TransitionInfo.Change pipChange) {
final int changeSize = info.getChanges().size();
if (changeSize < 4) {
throw new RuntimeException(
@@ -410,8 +592,35 @@
}
mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction);
startTransaction.apply();
+
+ mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
finishCallback.onTransitionFinished(null, null);
- return true;
+ }
+
+ private void updatePipForUnhandledTransition(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ if (mCurrentPipTaskToken == null) {
+ return;
+ }
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (!mCurrentPipTaskToken.equals(change.getContainer())) {
+ continue;
+ }
+ // When the PIP window is visible and being a part of the transition, such as display
+ // rotation, we need to update its bounds and rounded corner.
+ final SurfaceControl leash = change.getLeash();
+ final Rect destBounds = mPipBoundsState.getBounds();
+ final boolean isInPip = mPipTransitionState.isInPip();
+ mSurfaceTransactionHelper
+ .crop(startTransaction, leash, destBounds)
+ .round(startTransaction, leash, isInPip);
+ mSurfaceTransactionHelper
+ .crop(finishTransaction, leash, destBounds)
+ .round(finishTransaction, leash, isInPip);
+ break;
+ }
}
private void finishResizeForMenu(Rect destinationBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 1c8b9bc..22b3ef3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
+import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import android.annotation.Nullable;
import android.app.PictureInPictureParams;
@@ -69,6 +70,10 @@
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
return;
}
+ if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ animator::clearContentOverlay, true /* withStartDelay*/);
+ }
onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx);
sendOnPipTransitionFinished(direction);
}
@@ -76,6 +81,11 @@
@Override
public void onPipAnimationCancel(TaskInfo taskInfo,
PipAnimationController.PipTransitionAnimator animator) {
+ final int direction = animator.getTransitionDirection();
+ if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ animator::clearContentOverlay, true /* withStartDelay */);
+ }
sendOnPipTransitionCancelled(animator.getTransitionDirection());
}
};
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 4c28be0..83830ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -382,6 +382,8 @@
float splitRatio, RemoteAnimationAdapter adapter) {
// Init divider first to make divider leash for remote animation target.
mSplitLayout.init();
+ // Set false to avoid record new bounds with old task still on top;
+ mShouldUpdateRecents = false;
final WindowContainerTransaction wct = new WindowContainerTransaction();
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
@@ -406,6 +408,7 @@
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
+ mShouldUpdateRecents = true;
mSyncQueue.queue(evictWct);
mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
finishedCallback.onAnimationFinished();
@@ -428,6 +431,7 @@
@Override
public void onAnimationCancelled() {
+ mShouldUpdateRecents = true;
mSyncQueue.queue(evictWct);
mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
try {
@@ -834,7 +838,9 @@
mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape());
}
- updateRecentTasksSplitPair();
+ if (present && visible) {
+ updateRecentTasksSplitPair();
+ }
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onTaskStageChanged(taskId, stage, visible);
@@ -850,7 +856,6 @@
if (!mShouldUpdateRecents) {
return;
}
-
mRecentTasks.ifPresent(recentTasks -> {
Rect topLeftBounds = mSplitLayout.getBounds1();
Rect bottomRightBounds = mSplitLayout.getBounds2();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
index 0683a25..59eecb5d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
@@ -105,6 +105,9 @@
* @param leash surface leash for the appeared task
*/
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ // Only handle child task surface here.
+ if (!taskInfo.hasParentTask()) return;
+
AnimationContext context = new AnimationContext(leash);
mAnimationContextByTaskId.put(taskInfo.taskId, context);
}
@@ -114,6 +117,8 @@
* @param taskInfo info for the vanished task
*/
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (!taskInfo.hasParentTask()) return;
+
AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
if (context != null) {
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 711510d..33a98b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -359,6 +359,28 @@
return;
}
+ // apply transfer starting window directly if there is no other task change.
+ final int changeSize = info.getChanges().size();
+ if (changeSize == 2) {
+ boolean nonTaskChange = true;
+ boolean transferStartingWindow = false;
+ for (int i = changeSize - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null) {
+ nonTaskChange = false;
+ break;
+ }
+ if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+ transferStartingWindow = true;
+ }
+ }
+ if (nonTaskChange && transferStartingWindow) {
+ t.apply();
+ onFinish(transitionToken, null /* wct */, null /* wctCB */);
+ return;
+ }
+ }
+
final ActiveTransition active = mActiveTransitions.get(activeIdx);
active.mInfo = info;
active.mStartT = t;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index c4be785..68b0b4e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -17,11 +17,11 @@
@file:JvmName("CommonAssertions")
package com.android.wm.shell.flicker
-import android.graphics.Region
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.region.Region
fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index 623055f6..efae207 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -17,23 +17,23 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.graphics.Region
import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.region.Region
class AppPairsHelper(
instrumentation: Instrumentation,
activityLabel: String,
component: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, component) {
- fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region {
+ fun getPrimaryBounds(dividerBounds: Region): Region {
val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset)
return primaryAppBounds
}
- fun getSecondaryBounds(dividerBounds: Region): android.graphics.Region {
+ fun getSecondaryBounds(dividerBounds: Region): Region {
val displayBounds = WindowUtils.displayBounds
val secondaryAppBounds = Region(0,
dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 2357b0d..8e6fa5f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.graphics.Rect
import android.media.session.MediaController
import android.media.session.MediaSessionManager
import android.os.SystemClock
@@ -26,6 +25,7 @@
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
+import com.android.server.wm.traces.common.Rect
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 0448ec8..7d7add4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.graphics.Region
import android.util.Rational
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -41,6 +40,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.region.Region
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 0cbfc92..d3bb008 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -128,8 +128,8 @@
@Presubmit
@Test
fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -140,8 +140,8 @@
@Presubmit
@Test
fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index 3e7e2f5..f8a3aff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -45,8 +45,8 @@
@Presubmit
@Test
open fun pipAppWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -57,8 +57,8 @@
@Presubmit
@Test
open fun pipAppLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index cba677b..52177c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -78,8 +78,8 @@
@Presubmit
@Test
fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -90,8 +90,8 @@
@Presubmit
@Test
fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index 7ed0c49..f9e180e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -25,8 +25,8 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.traces.RegionSubject
import org.junit.Assume.assumeFalse
+import com.android.server.wm.flicker.traces.region.RegionSubject
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
index 6e0324c..0499e7d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.Presubmit
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.traces.RegionSubject
+import com.android.server.wm.flicker.traces.region.RegionSubject
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
@@ -66,8 +66,8 @@
@Presubmit
@Test
open fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -78,8 +78,8 @@
@Presubmit
@Test
open fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index 56c0949..b7bfa1b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -24,8 +24,8 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.traces.region.RegionSubject
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.traces.RegionSubject
import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 62e230f..c36dfda 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -88,9 +88,9 @@
@Presubmit
@Test
fun pipInVisibleBounds() {
- testSpec.assertWm {
+ testSpec.assertWmVisibleRegion(pipApp.component) {
val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
- coversAtMost(displayBounds, pipApp.component)
+ coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index e3f544a..df58194 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -101,8 +101,8 @@
@FlakyTest(bugId = 161435597)
@Test
fun pipWindowInsideDisplayBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -119,8 +119,8 @@
@FlakyTest(bugId = 161435597)
@Test
fun pipLayerInsideDisplayBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 0a3a849..16bc5075 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -109,6 +109,7 @@
mSpiedTransitionState = spy(new OneHandedState());
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
+ when(mMockDisplayController.getDisplayLayout(anyInt())).thenReturn(null);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
@@ -153,6 +154,13 @@
}
@Test
+ public void testNullDisplayLayout() {
+ mSpiedOneHandedController.updateDisplayLayout(0);
+
+ verify(mMockDisplayAreaOrganizer, never()).setDisplayLayout(any());
+ }
+
+ @Test
public void testStartOneHandedShouldTriggerScheduleOffset() {
mSpiedTransitionState.setState(STATE_NONE);
mSpiedOneHandedController.setOneHandedEnabled(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index ef16fd3..1d92a48 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -432,4 +432,11 @@
assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse();
}
+
+ @Test
+ public void testDisplayArea_setDisplayLayout_should_updateDisplayBounds() {
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+
+ verify(mSpiedDisplayAreaOrganizer).updateDisplayBounds();
+ }
}
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index a48d7f7..213f35a 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -127,6 +127,32 @@
return reinterpret_cast<jlong>(shaderFilter.release());
}
+static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
+ va_end(args);
+ return ret;
+}
+
+static jlong createRuntimeShaderEffect(JNIEnv* env, jobject, jlong shaderBuilderHandle,
+ jstring inputShaderName) {
+ SkRuntimeShaderBuilder* builder =
+ reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilderHandle);
+ ScopedUtfChars name(env, inputShaderName);
+
+ if (builder->child(name.c_str()).fChild == nullptr) {
+ ThrowIAEFmt(env,
+ "unable to find a uniform with the name '%s' of the correct "
+ "type defined by the provided RuntimeShader",
+ name.c_str());
+ return 0;
+ }
+
+ sk_sp<SkImageFilter> filter = SkImageFilters::RuntimeShader(*builder, name.c_str(), nullptr);
+ return reinterpret_cast<jlong>(filter.release());
+}
+
static void RenderEffect_safeUnref(SkImageFilter* filter) {
SkSafeUnref(filter);
}
@@ -136,15 +162,16 @@
}
static const JNINativeMethod gRenderEffectMethods[] = {
- {"nativeGetFinalizer", "()J", (void*)getRenderEffectFinalizer},
- {"nativeCreateOffsetEffect", "(FFJ)J", (void*)createOffsetEffect},
- {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect},
- {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
- {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
- {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
- {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
- {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect}
-};
+ {"nativeGetFinalizer", "()J", (void*)getRenderEffectFinalizer},
+ {"nativeCreateOffsetEffect", "(FFJ)J", (void*)createOffsetEffect},
+ {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect},
+ {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
+ {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
+ {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
+ {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
+ {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect},
+ {"nativeCreateRuntimeShaderEffect", "(JLjava/lang/String;)J",
+ (void*)createRuntimeShaderEffect}};
int register_android_graphics_RenderEffect(JNIEnv* env) {
android::RegisterMethodsOrDie(env, "android/graphics/RenderEffect",
diff --git a/location/java/android/location/GnssMeasurementRequest.java b/location/java/android/location/GnssMeasurementRequest.java
index f509252..71cb0e3 100644
--- a/location/java/android/location/GnssMeasurementRequest.java
+++ b/location/java/android/location/GnssMeasurementRequest.java
@@ -16,10 +16,14 @@
package android.location;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.TimeUtils;
+
+import com.android.internal.util.Preconditions;
import java.util.Objects;
@@ -29,13 +33,16 @@
public final class GnssMeasurementRequest implements Parcelable {
private final boolean mCorrelationVectorOutputsEnabled;
private final boolean mFullTracking;
+ private final int mIntervalMillis;
/**
* Creates a {@link GnssMeasurementRequest} with a full list of parameters.
*/
- private GnssMeasurementRequest(boolean fullTracking, boolean correlationVectorOutputsEnabled) {
+ private GnssMeasurementRequest(boolean fullTracking, boolean correlationVectorOutputsEnabled,
+ int intervalMillis) {
mFullTracking = fullTracking;
mCorrelationVectorOutputsEnabled = correlationVectorOutputsEnabled;
+ mIntervalMillis = intervalMillis;
}
/**
@@ -68,13 +75,26 @@
return mFullTracking;
}
+ /**
+ * Represents the requested time interval between the reported measurements in milliseconds.
+ *
+ * <p>If the time interval is not set, the default value is 0, which means the fastest rate the
+ * GNSS chipset can report.
+ *
+ * <p>The GNSS chipset may report measurements with a rate faster than requested.
+ */
+ public @IntRange(from = 0) int getIntervalMillis() {
+ return mIntervalMillis;
+ }
+
@NonNull
public static final Creator<GnssMeasurementRequest> CREATOR =
new Creator<GnssMeasurementRequest>() {
@Override
@NonNull
public GnssMeasurementRequest createFromParcel(@NonNull Parcel parcel) {
- return new GnssMeasurementRequest(parcel.readBoolean(), parcel.readBoolean());
+ return new GnssMeasurementRequest(parcel.readBoolean(), parcel.readBoolean(),
+ parcel.readInt());
}
@Override
@@ -87,6 +107,7 @@
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeBoolean(mFullTracking);
parcel.writeBoolean(mCorrelationVectorOutputsEnabled);
+ parcel.writeInt(mIntervalMillis);
}
@NonNull
@@ -94,11 +115,13 @@
public String toString() {
StringBuilder s = new StringBuilder();
s.append("GnssMeasurementRequest[");
+ s.append("@");
+ TimeUtils.formatDuration(mIntervalMillis, s);
if (mFullTracking) {
- s.append("FullTracking");
+ s.append(", FullTracking");
}
if (mCorrelationVectorOutputsEnabled) {
- s.append(", CorrelationVectorOutPuts");
+ s.append(", CorrelationVectorOutputs");
}
s.append(']');
return s.toString();
@@ -115,12 +138,15 @@
if (mCorrelationVectorOutputsEnabled != other.mCorrelationVectorOutputsEnabled) {
return false;
}
+ if (mIntervalMillis != other.mIntervalMillis) {
+ return false;
+ }
return true;
}
@Override
public int hashCode() {
- return Objects.hash(mFullTracking, mCorrelationVectorOutputsEnabled);
+ return Objects.hash(mFullTracking, mCorrelationVectorOutputsEnabled, mIntervalMillis);
}
@Override
@@ -132,6 +158,7 @@
public static final class Builder {
private boolean mCorrelationVectorOutputsEnabled;
private boolean mFullTracking;
+ private int mIntervalMillis;
/**
* Constructs a {@link Builder} instance.
@@ -145,6 +172,7 @@
public Builder(@NonNull GnssMeasurementRequest request) {
mCorrelationVectorOutputsEnabled = request.isCorrelationVectorOutputsEnabled();
mFullTracking = request.isFullTracking();
+ mIntervalMillis = request.getIntervalMillis();
}
/**
@@ -183,10 +211,25 @@
return this;
}
+ /**
+ * Set the time interval between the reported measurements in milliseconds, which is 0 by
+ * default.
+ *
+ * <p>An interval of 0 milliseconds means the fastest rate the chipset can report.
+ *
+ * <p>The GNSS chipset may report measurements with a rate faster than requested.
+ */
+ @NonNull public Builder setIntervalMillis(@IntRange(from = 0) int value) {
+ mIntervalMillis = Preconditions.checkArgumentInRange(value, 0, Integer.MAX_VALUE,
+ "intervalMillis");
+ return this;
+ }
+
/** Builds a {@link GnssMeasurementRequest} instance as specified by this builder. */
@NonNull
public GnssMeasurementRequest build() {
- return new GnssMeasurementRequest(mFullTracking, mCorrelationVectorOutputsEnabled);
+ return new GnssMeasurementRequest(mFullTracking, mCorrelationVectorOutputsEnabled,
+ mIntervalMillis);
}
}
}
diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java
index aeca562..262630b 100644
--- a/location/java/android/location/GnssSingleSatCorrection.java
+++ b/location/java/android/location/GnssSingleSatCorrection.java
@@ -26,6 +26,8 @@
import com.android.internal.util.Preconditions;
+import java.util.Objects;
+
/**
* A container with measurement corrections for a single visible satellite
*
@@ -119,15 +121,17 @@
@Nullable
private final GnssReflectingPlane mReflectingPlane;
- private GnssSingleSatCorrection(Builder builder) {
- mSingleSatCorrectionFlags = builder.mSingleSatCorrectionFlags;
- mSatId = builder.mSatId;
- mConstellationType = builder.mConstellationType;
- mCarrierFrequencyHz = builder.mCarrierFrequencyHz;
- mProbSatIsLos = builder.mProbSatIsLos;
- mExcessPathLengthMeters = builder.mExcessPathLengthMeters;
- mExcessPathLengthUncertaintyMeters = builder.mExcessPathLengthUncertaintyMeters;
- mReflectingPlane = builder.mReflectingPlane;
+ private GnssSingleSatCorrection(int singleSatCorrectionFlags, int constellationType, int satId,
+ float carrierFrequencyHz, float probSatIsLos, float excessPathLengthMeters,
+ float excessPathLengthUncertaintyMeters, GnssReflectingPlane reflectingPlane) {
+ mSingleSatCorrectionFlags = singleSatCorrectionFlags;
+ mConstellationType = constellationType;
+ mSatId = satId;
+ mCarrierFrequencyHz = carrierFrequencyHz;
+ mProbSatIsLos = probSatIsLos;
+ mExcessPathLengthMeters = excessPathLengthMeters;
+ mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
+ mReflectingPlane = reflectingPlane;
}
/**
@@ -239,27 +243,49 @@
return 0;
}
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSingleSatCorrectionFlags);
+ parcel.writeInt(mConstellationType);
+ parcel.writeInt(mSatId);
+ parcel.writeFloat(mCarrierFrequencyHz);
+ if (hasValidSatelliteLineOfSight()) {
+ parcel.writeFloat(mProbSatIsLos);
+ }
+ if (hasExcessPathLength()) {
+ parcel.writeFloat(mExcessPathLengthMeters);
+ }
+ if (hasExcessPathLengthUncertainty()) {
+ parcel.writeFloat(mExcessPathLengthUncertaintyMeters);
+ }
+ if (hasReflectingPlane()) {
+ mReflectingPlane.writeToParcel(parcel, flags);
+ }
+ }
+
public static final Creator<GnssSingleSatCorrection> CREATOR =
new Creator<GnssSingleSatCorrection>() {
@Override
@NonNull
public GnssSingleSatCorrection createFromParcel(@NonNull Parcel parcel) {
- int mSingleSatCorrectionFlags = parcel.readInt();
- boolean hasReflectingPlane =
- (mSingleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0;
- final GnssSingleSatCorrection.Builder singleSatCorrectionBuilder =
- new Builder()
- .setConstellationType(parcel.readInt())
- .setSatelliteId(parcel.readInt())
- .setCarrierFrequencyHz(parcel.readFloat())
- .setProbabilityLineOfSight(parcel.readFloat())
- .setExcessPathLengthMeters(parcel.readFloat())
- .setExcessPathLengthUncertaintyMeters(parcel.readFloat());
- if (hasReflectingPlane) {
- singleSatCorrectionBuilder.setReflectingPlane(
- GnssReflectingPlane.CREATOR.createFromParcel(parcel));
- }
- return singleSatCorrectionBuilder.build();
+ int singleSatCorrectionFlags = parcel.readInt();
+ int constellationType = parcel.readInt();
+ int satId = parcel.readInt();
+ float carrierFrequencyHz = parcel.readFloat();
+ float probSatIsLos = (singleSatCorrectionFlags & HAS_PROB_SAT_IS_LOS_MASK) != 0
+ ? parcel.readFloat() : 0;
+ float excessPathLengthMeters =
+ (singleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0
+ ? parcel.readFloat() : 0;
+ float excessPathLengthUncertaintyMeters =
+ (singleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0
+ ? parcel.readFloat() : 0;
+ GnssReflectingPlane reflectingPlane =
+ (singleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0
+ ? GnssReflectingPlane.CREATOR.createFromParcel(parcel) : null;
+ return new GnssSingleSatCorrection(singleSatCorrectionFlags, constellationType,
+ satId, carrierFrequencyHz, probSatIsLos, excessPathLengthMeters,
+ excessPathLengthUncertaintyMeters, reflectingPlane);
}
@Override
@@ -268,41 +294,94 @@
}
};
- @NonNull
@Override
- public String toString() {
- final String format = " %-29s = %s\n";
- StringBuilder builder = new StringBuilder("GnssSingleSatCorrection:\n");
- builder.append(
- String.format(format, "SingleSatCorrectionFlags = ", mSingleSatCorrectionFlags));
- builder.append(String.format(format, "ConstellationType = ", mConstellationType));
- builder.append(String.format(format, "SatId = ", mSatId));
- builder.append(String.format(format, "CarrierFrequencyHz = ", mCarrierFrequencyHz));
- builder.append(String.format(format, "ProbSatIsLos = ", mProbSatIsLos));
- builder.append(String.format(format, "ExcessPathLengthMeters = ", mExcessPathLengthMeters));
- builder.append(
- String.format(
- format,
- "ExcessPathLengthUncertaintyMeters = ",
- mExcessPathLengthUncertaintyMeters));
- if (hasReflectingPlane()) {
- builder.append(String.format(format, "ReflectingPlane = ", mReflectingPlane));
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
}
- return builder.toString();
+ if (!(obj instanceof GnssSingleSatCorrection)) {
+ return false;
+ }
+
+ GnssSingleSatCorrection other = (GnssSingleSatCorrection) obj;
+ if (mConstellationType != other.mConstellationType) {
+ return false;
+ }
+ if (mSatId != other.mSatId) {
+ return false;
+ }
+ if (Float.compare(mCarrierFrequencyHz, other.mCarrierFrequencyHz) != 0) {
+ return false;
+ }
+
+ if (hasValidSatelliteLineOfSight() != other.hasValidSatelliteLineOfSight()) {
+ return false;
+ }
+ if (hasValidSatelliteLineOfSight()
+ && Float.compare(mProbSatIsLos, other.mProbSatIsLos) != 0) {
+ return false;
+ }
+
+ if (hasExcessPathLength() != other.hasExcessPathLength()) {
+ return false;
+ }
+ if (hasExcessPathLength()
+ && Float.compare(mExcessPathLengthMeters, other.mExcessPathLengthMeters) != 0) {
+ return false;
+ }
+
+ if (hasExcessPathLengthUncertainty() != other.hasExcessPathLengthUncertainty()) {
+ return false;
+ }
+ if (hasExcessPathLengthUncertainty() && Float.compare(mExcessPathLengthUncertaintyMeters,
+ other.mExcessPathLengthUncertaintyMeters) != 0) {
+ return false;
+ }
+
+ if (hasReflectingPlane() != other.hasReflectingPlane()) {
+ return false;
+ }
+ if (hasReflectingPlane()
+ && !mReflectingPlane.equals(other.mReflectingPlane)) {
+ return false;
+ }
+ return true;
}
@Override
- public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeInt(mSingleSatCorrectionFlags);
- parcel.writeInt(mConstellationType);
- parcel.writeInt(mSatId);
- parcel.writeFloat(mCarrierFrequencyHz);
- parcel.writeFloat(mProbSatIsLos);
- parcel.writeFloat(mExcessPathLengthMeters);
- parcel.writeFloat(mExcessPathLengthUncertaintyMeters);
- if (hasReflectingPlane()) {
- mReflectingPlane.writeToParcel(parcel, flags);
+ public int hashCode() {
+ return Objects.hash(mSingleSatCorrectionFlags,
+ mConstellationType,
+ mSatId,
+ mCarrierFrequencyHz,
+ mProbSatIsLos,
+ mExcessPathLengthMeters,
+ mExcessPathLengthUncertaintyMeters,
+ mReflectingPlane);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssSingleSatCorrection:[");
+ builder.append(" ConstellationType=").append(mConstellationType);
+ builder.append(" SatId=").append(mSatId);
+ builder.append(" CarrierFrequencyHz=").append(mCarrierFrequencyHz);
+ if (hasValidSatelliteLineOfSight()) {
+ builder.append(" ProbSatIsLos=").append(mProbSatIsLos);
}
+ if (hasExcessPathLength()) {
+ builder.append(" ExcessPathLengthMeters=").append(mExcessPathLengthMeters);
+ }
+ if (hasExcessPathLengthUncertainty()) {
+ builder.append(" ExcessPathLengthUncertaintyMeters=").append(
+ mExcessPathLengthUncertaintyMeters);
+ }
+ if (hasReflectingPlane()) {
+ builder.append(" ReflectingPlane=").append(mReflectingPlane);
+ }
+ builder.append(']');
+ return builder.toString();
}
/** Builder for {@link GnssSingleSatCorrection} */
@@ -332,6 +411,7 @@
/** Sets the Satellite ID defined in the ICD of the given constellation. */
@NonNull public Builder setSatelliteId(@IntRange(from = 0) int satId) {
+ Preconditions.checkArgumentNonnegative(satId, "satId should be non-negative.");
mSatId = satId;
return this;
}
@@ -339,6 +419,8 @@
/** Sets the Carrier frequency in Hz. */
@NonNull public Builder setCarrierFrequencyHz(
@FloatRange(from = 0.0f, fromInclusive = false) float carrierFrequencyHz) {
+ Preconditions.checkArgument(
+ carrierFrequencyHz >= 0, "carrierFrequencyHz should be non-negative.");
mCarrierFrequencyHz = carrierFrequencyHz;
return this;
}
@@ -352,8 +434,18 @@
Preconditions.checkArgumentInRange(
probSatIsLos, 0, 1, "probSatIsLos should be between 0 and 1.");
mProbSatIsLos = probSatIsLos;
- mSingleSatCorrectionFlags =
- (byte) (mSingleSatCorrectionFlags | HAS_PROB_SAT_IS_LOS_MASK);
+ mSingleSatCorrectionFlags |= HAS_PROB_SAT_IS_LOS_MASK;
+ return this;
+ }
+
+ /**
+ * Clears the line-of-sight probability of the satellite at the given location.
+ *
+ * <p>This is to negate {@link #setProbabilityLineOfSight} call.
+ */
+ @NonNull public Builder clearProbabilityLineOfSight() {
+ mProbSatIsLos = 0;
+ mSingleSatCorrectionFlags &= ~HAS_PROB_SAT_IS_LOS_MASK;
return this;
}
@@ -363,18 +455,42 @@
*/
@NonNull public Builder setExcessPathLengthMeters(
@FloatRange(from = 0.0f) float excessPathLengthMeters) {
+ Preconditions.checkArgument(excessPathLengthMeters >= 0,
+ "excessPathLengthMeters should be non-negative.");
mExcessPathLengthMeters = excessPathLengthMeters;
- mSingleSatCorrectionFlags =
- (byte) (mSingleSatCorrectionFlags | HAS_EXCESS_PATH_LENGTH_MASK);
+ mSingleSatCorrectionFlags |= HAS_EXCESS_PATH_LENGTH_MASK;
+ return this;
+ }
+
+ /**
+ * Clears the Excess path length.
+ *
+ * <p>This is to negate {@link #setExcessPathLengthMeters} call.
+ */
+ @NonNull public Builder clearExcessPathLengthMeters() {
+ mExcessPathLengthMeters = 0;
+ mSingleSatCorrectionFlags &= ~HAS_EXCESS_PATH_LENGTH_MASK;
return this;
}
/** Sets the error estimate (1-sigma) for the Excess path length estimate */
@NonNull public Builder setExcessPathLengthUncertaintyMeters(
@FloatRange(from = 0.0f) float excessPathLengthUncertaintyMeters) {
+ Preconditions.checkArgument(excessPathLengthUncertaintyMeters >= 0,
+ "excessPathLengthUncertaintyMeters should be non-negative.");
mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
- mSingleSatCorrectionFlags =
- (byte) (mSingleSatCorrectionFlags | HAS_EXCESS_PATH_LENGTH_UNC_MASK);
+ mSingleSatCorrectionFlags |= HAS_EXCESS_PATH_LENGTH_UNC_MASK;
+ return this;
+ }
+
+ /**
+ * Clears the error estimate (1-sigma) for the Excess path length estimate
+ *
+ * <p>This is to negate {@link #setExcessPathLengthUncertaintyMeters} call.
+ */
+ @NonNull public Builder clearExcessPathLengthUncertaintyMeters() {
+ mExcessPathLengthUncertaintyMeters = 0;
+ mSingleSatCorrectionFlags &= ~HAS_EXCESS_PATH_LENGTH_UNC_MASK;
return this;
}
@@ -382,18 +498,23 @@
@NonNull public Builder setReflectingPlane(@Nullable GnssReflectingPlane reflectingPlane) {
mReflectingPlane = reflectingPlane;
if (reflectingPlane != null) {
- mSingleSatCorrectionFlags =
- (byte) (mSingleSatCorrectionFlags | HAS_REFLECTING_PLANE_MASK);
+ mSingleSatCorrectionFlags |= HAS_REFLECTING_PLANE_MASK;
} else {
- mSingleSatCorrectionFlags =
- (byte) (mSingleSatCorrectionFlags & ~HAS_REFLECTING_PLANE_MASK);
+ mSingleSatCorrectionFlags &= ~HAS_REFLECTING_PLANE_MASK;
}
return this;
}
/** Builds a {@link GnssSingleSatCorrection} instance as specified by this builder. */
@NonNull public GnssSingleSatCorrection build() {
- return new GnssSingleSatCorrection(this);
+ return new GnssSingleSatCorrection(mSingleSatCorrectionFlags,
+ mConstellationType,
+ mSatId,
+ mCarrierFrequencyHz,
+ mProbSatIsLos,
+ mExcessPathLengthMeters,
+ mExcessPathLengthUncertaintyMeters,
+ mReflectingPlane);
}
}
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 9993ce9..85e49cc 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1187,6 +1187,9 @@
case AudioSystem.STREAM_ACCESSIBILITY:
mContentType = CONTENT_TYPE_SPEECH;
break;
+ case AudioSystem.STREAM_ASSISTANT:
+ mContentType = CONTENT_TYPE_SPEECH;
+ break;
default:
Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes");
}
@@ -1611,6 +1614,8 @@
return USAGE_VOICE_COMMUNICATION_SIGNALLING;
case AudioSystem.STREAM_ACCESSIBILITY:
return USAGE_ASSISTANCE_ACCESSIBILITY;
+ case AudioSystem.STREAM_ASSISTANT:
+ return USAGE_ASSISTANT;
case AudioSystem.STREAM_TTS:
default:
return USAGE_UNKNOWN;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 5c4211f..21fc6ec 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1204,7 +1204,8 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
public int getLastAudibleStreamVolume(int streamType) {
final IAudioService service = getService();
try {
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index c912759..1f89f99 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -145,10 +145,8 @@
final int match_rule = mRule & ~RULE_EXCLUSION_MASK;
switch (match_rule) {
case RULE_MATCH_ATTRIBUTE_USAGE:
- dest.writeInt(mAttr.getSystemUsage());
- break;
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
- dest.writeInt(mAttr.getCapturePreset());
+ mAttr.writeToParcel(dest, AudioAttributes.FLATTEN_TAGS/*flags*/);
break;
case RULE_MATCH_UID:
case RULE_MATCH_USERID:
@@ -266,12 +264,14 @@
public boolean isForCallRedirection() {
for (AudioMixMatchCriterion criterion : mCriteria) {
if (criterion.mAttr != null
- && (criterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE
- && criterion.mAttr.getUsage() == AudioAttributes.USAGE_VOICE_COMMUNICATION)
+ && criterion.mAttr.isForCallRedirection()
+ && ((criterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE
+ && (criterion.mAttr.getUsage() == AudioAttributes.USAGE_VOICE_COMMUNICATION
+ || criterion.mAttr.getUsage()
+ == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING))
|| (criterion.mRule == RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET
- && criterion.mAttr.getCapturePreset()
- == MediaRecorder.AudioSource.VOICE_COMMUNICATION)
- && criterion.mAttr.isForCallRedirection()) {
+ && (criterion.mAttr.getCapturePreset()
+ == MediaRecorder.AudioSource.VOICE_COMMUNICATION)))) {
return true;
}
}
@@ -713,19 +713,8 @@
Integer intProp = null;
switch (match_rule) {
case RULE_MATCH_ATTRIBUTE_USAGE:
- int usage = in.readInt();
- if (AudioAttributes.isSystemUsage(usage)) {
- attr = new AudioAttributes.Builder()
- .setSystemUsage(usage).build();
- } else {
- attr = new AudioAttributes.Builder()
- .setUsage(usage).build();
- }
- break;
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
- int preset = in.readInt();
- attr = new AudioAttributes.Builder()
- .setInternalCapturePreset(preset).build();
+ attr = AudioAttributes.CREATOR.createFromParcel(in);
break;
case RULE_MATCH_UID:
case RULE_MATCH_USERID:
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 7c08913..4128abf 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -19,6 +19,7 @@
import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -688,6 +689,8 @@
private native Filter nativeOpenFilter(int type, int subType, long bufferSize);
private native TimeFilter nativeOpenTimeFilter();
private native String nativeGetFrontendHardwareInfo();
+ private native int nativeSetMaxNumberOfFrontends(int frontendType, int maxNumber);
+ private native int nativeGetMaxNumberOfFrontends(int frontendType);
private native Lnb nativeOpenLnbByHandle(int handle);
private native Lnb nativeOpenLnbByName(String name);
@@ -1291,7 +1294,7 @@
* @throws IllegalStateException if there is no active frontend currently.
*/
@Nullable
- public String getCurrentFrontendHardwardInfo() {
+ public String getCurrentFrontendHardwareInfo() {
mFrontendLock.lock();
try {
if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
@@ -1307,6 +1310,55 @@
}
}
+ /**
+ * Sets the maximum usable frontends number of a given frontend type. It is used to enable or
+ * disable frontends when cable connection status is changed by user.
+ *
+ * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would return
+ * {@link RESULT_UNAVAILABLE}. Use {@link TunerVersionChecker#getTunerVersion()} to check the
+ * version.
+ *
+ * @param frontendType the {@link android.media.tv.tuner.frontend.FrontendSettings.Type} which
+ * the maximum usable number will be set.
+ * @param maxNumber the new maximum usable number.
+ * @return result status of the operation.
+ */
+ @Result
+ public int setMaxNumberOfFrontends(
+ @FrontendSettings.Type int frontendType, @IntRange(from = 0) int maxNumber) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "Set maximum Frontends")) {
+ return RESULT_UNAVAILABLE;
+ }
+ if (maxNumber < 0) {
+ return RESULT_INVALID_ARGUMENT;
+ }
+ int res = nativeSetMaxNumberOfFrontends(frontendType, maxNumber);
+ if (res == RESULT_SUCCESS) {
+ // TODO: b/211778848 Update Tuner Resource Manager.
+ }
+ return res;
+ }
+
+ /**
+ * Get the maximum usable frontends number of a given frontend type.
+ *
+ * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would return
+ * {@code -1}. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * @param frontendType the {@link android.media.tv.tuner.frontend.FrontendSettings.Type} which
+ * the maximum usable number will be queried.
+ * @return the maximum usable number of the queried frontend type.
+ */
+ @IntRange(from = -1)
+ public int getMaxNumberOfFrontends(@FrontendSettings.Type int frontendType) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "Set maximum Frontends")) {
+ return -1;
+ }
+ return nativeGetMaxNumberOfFrontends(frontendType);
+ }
+
/** @hide */
public FrontendInfo getFrontendInfoById(int id) {
mFrontendLock.lock();
@@ -1382,7 +1434,7 @@
}
}
- private void onUnLocked() {
+ private void onUnlocked() {
Log.d(TAG, "Wrote Stats Log for unlocked event from scanning.");
FrameworkStatsLog.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED);
@@ -1392,7 +1444,7 @@
mScanCallbackExecutor.execute(() -> {
synchronized (mScanCallbackLock) {
if (mScanCallback != null) {
- mScanCallback.onUnLocked();
+ mScanCallback.onUnlocked();
}
}
});
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index 6bbc13fe..68f3c1b 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -37,7 +37,7 @@
void onLocked();
/** Scan unlocked the signal. */
- default void onUnLocked() {}
+ default void onUnlocked() {}
/** Scan stopped. */
void onScanStopped();
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index f5606bc..c601649 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1044,7 +1044,7 @@
} else {
env->CallVoidMethod(
frontend,
- env->GetMethodID(clazz, "onUnLocked", "()V"));
+ env->GetMethodID(clazz, "onUnlocked", "()V"));
}
break;
}
@@ -1577,6 +1577,24 @@
return mFeClient->getHardwareInfo(info);
}
+jint JTuner::setMaxNumberOfFrontends(int32_t type, int32_t maxNumber) {
+ if (mTunerClient == nullptr) {
+ ALOGE("tuner is not initialized");
+ return (jint)Result::INVALID_STATE;
+ }
+
+ return (jint)mTunerClient->setMaxNumberOfFrontends(static_cast<FrontendType>(type), maxNumber);
+}
+
+int32_t JTuner::getMaxNumberOfFrontends(int32_t type) {
+ if (mTunerClient == nullptr) {
+ ALOGE("tuner is not initialized");
+ return -1;
+ }
+
+ return mTunerClient->getMaxNumberOfFrontends(static_cast<FrontendType>(type));
+}
+
jobject JTuner::openLnbByHandle(int handle) {
if (mTunerClient == nullptr) {
return nullptr;
@@ -4281,6 +4299,17 @@
return env->NewStringUTF(info.data());
}
+static jint android_media_tv_Tuner_set_maximum_frontends(JNIEnv *env, jobject thiz, jint type,
+ jint maxNumber) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->setMaxNumberOfFrontends(type, maxNumber);
+}
+
+static jint android_media_tv_Tuner_get_maximum_frontends(JNIEnv *env, jobject thiz, jint type) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->getMaxNumberOfFrontends(type);
+}
+
static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->closeFrontend();
@@ -4592,6 +4621,10 @@
(void *)android_media_tv_Tuner_open_shared_filter},
{ "nativeGetFrontendHardwareInfo","()Ljava/lang/String;",
(void *)android_media_tv_Tuner_get_frontend_hardware_info },
+ { "nativeSetMaxNumberOfFrontends", "(II)I",
+ (void *)android_media_tv_Tuner_set_maximum_frontends },
+ { "nativeGetMaxNumberOfFrontends", "(I)I",
+ (void *)android_media_tv_Tuner_get_maximum_frontends },
};
static const JNINativeMethod gFilterMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 4cad92b..f1b31e3 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -201,6 +201,8 @@
jint closeFrontend();
jint closeDemux();
Result getFrontendHardwareInfo(string& info);
+ jint setMaxNumberOfFrontends(int32_t frontendType, int32_t maxNumber);
+ int32_t getMaxNumberOfFrontends(int32_t frontendType);
jweak getObject();
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index f917f01..3c8fdfe6 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -194,4 +194,23 @@
return Result::INVALID_STATE;
}
+Result TunerClient::setMaxNumberOfFrontends(FrontendType frontendType, int32_t maxNumber) {
+ if (mTunerService != nullptr) {
+ Status s = mTunerService->setMaxNumberOfFrontends(frontendType, maxNumber);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
+
+ return Result::INVALID_STATE;
+}
+
+int TunerClient::getMaxNumberOfFrontends(FrontendType frontendType) {
+ if (mTunerService != nullptr) {
+ int32_t maxNumber;
+ mTunerService->getMaxNumberOfFrontends(frontendType, &maxNumber);
+ return maxNumber;
+ }
+
+ return -1;
+}
+
} // namespace android
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 37b8ee1..a9f37e6 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -32,6 +32,7 @@
using ::aidl::android::hardware::tv::tuner::DemuxCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendInfo;
+using ::aidl::android::hardware::tv::tuner::FrontendType;
using ::aidl::android::hardware::tv::tuner::Result;
using ::aidl::android::media::tv::tuner::ITunerService;
@@ -132,6 +133,21 @@
*/
Result setLna(bool bEnable);
+ /**
+ * Set the maximum frontend number of a given frontend type.
+ *
+ * @param frontendType the frontend type which maximum number will be set.
+ * @param maxNumber the new maximum number.
+ */
+ Result setMaxNumberOfFrontends(FrontendType frontendType, int32_t maxNumber);
+
+ /**
+ * Get the maximum frontend number of a given frontend type.
+ *
+ * @param frontendType the frontend type which maximum number will be queried.
+ */
+ int getMaxNumberOfFrontends(FrontendType frontendType);
+
private:
/**
* An AIDL Tuner Service Singleton assigned at the first time the Tuner Client
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index 03606ba..9039011 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -20,7 +20,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.bluetoothmidiservice"
>
- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
<uses-feature android:name="android.hardware.bluetooth_le"
android:required="true"/>
diff --git a/media/packages/BluetoothMidiService/AndroidManifestBase.xml b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
index bfb0546..5a900c7 100644
--- a/media/packages/BluetoothMidiService/AndroidManifestBase.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
@@ -19,7 +19,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.bluetoothmidiservice"
>
- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
<application
android:label="BluetoothMidi"
android:defaultToDeviceProtectedStorage="true"
diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp
index 2b81200..d80317b 100644
--- a/omapi/aidl/Android.bp
+++ b/omapi/aidl/Android.bp
@@ -28,8 +28,5 @@
rust: {
enabled: true,
},
- ndk: {
- separate_platform_variant: false,
- },
},
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index 181a594..b00fea4 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -83,10 +83,7 @@
*/
// TODO: Rename TAG_ALL to TAG_ANY.
public static final int TAG_ALL = -1;
- /**
- * {@link #set} value for all sets combined, not including debug sets.
- * @hide
- */
+ /** {@link #set} value for all sets combined, not including debug sets. */
public static final int SET_ALL = -1;
/** {@link #set} value where background data is accounted. */
public static final int SET_DEFAULT = 0;
@@ -114,9 +111,6 @@
SET_ALL,
SET_DEFAULT,
SET_FOREGROUND,
- SET_DEBUG_START,
- SET_DBG_VPN_IN,
- SET_DBG_VPN_OUT
})
public @interface State {
}
@@ -131,10 +125,7 @@
// TODO: Rename TAG_NONE to TAG_ALL.
public static final int TAG_NONE = 0;
- /**
- * {@link #metered} value to account for all metered states.
- * @hide
- */
+ /** {@link #metered} value to account for all metered states. */
public static final int METERED_ALL = -1;
/** {@link #metered} value where native, unmetered data is accounted. */
public static final int METERED_NO = 0;
@@ -152,10 +143,7 @@
}
- /**
- * {@link #roaming} value to account for all roaming states.
- * @hide
- */
+ /** {@link #roaming} value to account for all roaming states. */
public static final int ROAMING_ALL = -1;
/** {@link #roaming} value where native, non-roaming data is accounted. */
public static final int ROAMING_NO = 0;
@@ -172,10 +160,7 @@
public @interface Roaming {
}
- /**
- * {@link #onDefaultNetwork} value to account for all default network states.
- * @hide
- */
+ /** {@link #onDefaultNetwork} value to account for all default network states. */
public static final int DEFAULT_NETWORK_ALL = -1;
/** {@link #onDefaultNetwork} value to account for usage while not the default network. */
public static final int DEFAULT_NETWORK_NO = 0;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index 659ad06..e9084b0 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -16,6 +16,7 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
@@ -39,7 +40,9 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.net.wifi.WifiInfo;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -70,7 +73,7 @@
*
* @hide
*/
-// @SystemApi(client = MODULE_LIBRARIES)
+@SystemApi(client = MODULE_LIBRARIES)
public final class NetworkTemplate implements Parcelable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -572,6 +575,7 @@
/**
* Get subscriber Id of the template.
+ * @hide
*/
@Nullable
@UnsupportedAppUsage
@@ -588,26 +592,19 @@
}
/**
- * Get Wifi Network Key of the template. See {@link WifiInfo#getCurrentNetworkKey()}.
+ * Get the set of Wifi Network Keys of the template.
+ * See {@link WifiInfo#getCurrentNetworkKey()}.
*/
- @Nullable
- public String getWifiNetworkKey() {
- return CollectionUtils.isEmpty(mMatchWifiNetworkKeys) ? null : mMatchWifiNetworkKeys[0];
- }
-
- /**
- * Get set of Wifi Network Keys of the template.
- */
- @Nullable
+ @NonNull
public Set<String> getWifiNetworkKeys() {
return new ArraySet<>(Arrays.asList(mMatchWifiNetworkKeys));
}
/** @hide */
- // TODO: Remove this and replace all callers with {@link #getWifiNetworkKey()}.
+ // TODO: Remove this and replace all callers with {@link #getWifiNetworkKeys()}.
@Nullable
public String getNetworkId() {
- return getWifiNetworkKey();
+ return getWifiNetworkKeys().isEmpty() ? null : getWifiNetworkKeys().iterator().next();
}
/**
diff --git a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
index d1e432e..179d945 100644
--- a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
@@ -1236,37 +1236,53 @@
int callingUid = Binder.getCallingUid();
UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
final int resourceId = mNextResourceId++;
- FileDescriptor sockFd = null;
+
+ ParcelFileDescriptor pFd = null;
try {
if (!userRecord.mSocketQuotaTracker.isAvailable()) {
return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
- sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- mUidFdTagger.tag(sockFd, callingUid);
+ FileDescriptor sockFd = null;
+ try {
+ sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ pFd = ParcelFileDescriptor.dup(sockFd);
+ } finally {
+ IoUtils.closeQuietly(sockFd);
+ }
+ mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
// This code is common to both the unspecified and specified port cases
Os.setsockoptInt(
- sockFd,
+ pFd.getFileDescriptor(),
OsConstants.IPPROTO_UDP,
OsConstants.UDP_ENCAP,
OsConstants.UDP_ENCAP_ESPINUDP);
- mNetd.ipSecSetEncapSocketOwner(new ParcelFileDescriptor(sockFd), callingUid);
+ mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
if (port != 0) {
Log.v(TAG, "Binding to port " + port);
- Os.bind(sockFd, INADDR_ANY, port);
+ Os.bind(pFd.getFileDescriptor(), INADDR_ANY, port);
} else {
- port = bindToRandomPort(sockFd);
+ port = bindToRandomPort(pFd.getFileDescriptor());
}
userRecord.mEncapSocketRecords.put(
resourceId,
new RefcountedResource<EncapSocketRecord>(
- new EncapSocketRecord(resourceId, sockFd, port), binder));
- return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
+ new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port),
+ binder));
+ return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
+ pFd.getFileDescriptor());
} catch (IOException | ErrnoException e) {
- IoUtils.closeQuietly(sockFd);
+ try {
+ if (pFd != null) {
+ pFd.close();
+ }
+ } catch (IOException ex) {
+ // Nothing can be done at this point
+ Log.e(TAG, "Failed to close pFd.");
+ }
}
// If we make it to here, then something has gone wrong and we couldn't open a socket.
// The only reasonable condition that would cause that is resource unavailable.
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
index c7c8893..bb123a3 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
@@ -24,11 +24,11 @@
import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.INetd;
import android.net.NetworkStats;
import android.net.UnderlyingNetworkInfo;
-import android.net.util.NetdService;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -70,7 +70,7 @@
private final boolean mUseBpfStats;
- private INetd mNetdService;
+ private final INetd mNetd;
/**
* Guards persistent data access in this class
@@ -158,12 +158,12 @@
NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces);
}
- public NetworkStatsFactory() {
- this(new File("/proc/"), true);
+ public NetworkStatsFactory(@NonNull INetd netd) {
+ this(new File("/proc/"), true, netd);
}
@VisibleForTesting
- public NetworkStatsFactory(File procRoot, boolean useBpfStats) {
+ public NetworkStatsFactory(File procRoot, boolean useBpfStats, @NonNull INetd netd) {
mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
@@ -172,6 +172,7 @@
mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
+ mNetd = netd;
}
public NetworkStats readBpfNetworkStatsDev() throws IOException {
@@ -298,10 +299,7 @@
// Ask netd to do a active map stats swap. When the binder call successfully returns,
// the system server should be able to safely read and clean the inactive map
// without race problem.
- if (mNetdService == null) {
- mNetdService = NetdService.getInstance();
- }
- mNetdService.trafficSwapActiveStatsMap();
+ mNetd.trafficSwapActiveStatsMap();
}
/**
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index a3de9e4..ced2e22 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -42,7 +42,6 @@
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
-import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UNSUPPORTED;
@@ -89,6 +88,7 @@
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.DataUsageRequest;
+import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
@@ -106,13 +106,13 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
+import android.net.TetheringManager;
import android.net.TrafficStats;
import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
-import android.os.BestClock;
import android.os.Binder;
import android.os.DropBoxManager;
import android.os.Environment;
@@ -148,6 +148,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FileRotator;
+import com.android.net.module.util.BestClock;
import com.android.net.module.util.BinderUtils;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStatsUtils;
@@ -410,10 +411,11 @@
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock =
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
+ final INetd netd = INetd.Stub.asInterface(
+ (IBinder) context.getSystemService(Context.NETD_SERVICE));
final NetworkStatsService service = new NetworkStatsService(context, networkManager,
alarmManager, wakeLock, getDefaultClock(),
- new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(),
+ new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd),
new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
new Dependencies());
service.registerLocalService();
@@ -529,8 +531,9 @@
}
// watch for tethering changes
- final IntentFilter tetherFilter = new IntentFilter(ACTION_TETHER_STATE_CHANGED);
- mContext.registerReceiver(mTetherReceiver, tetherFilter, null, mHandler);
+ final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
+ tetheringManager.registerTetheringEventCallback(
+ new HandlerExecutor(mHandler), mTetherListener);
// listen for periodic polling events
final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
@@ -586,7 +589,8 @@
@GuardedBy("mStatsLock")
private void shutdownLocked() {
- mContext.unregisterReceiver(mTetherReceiver);
+ final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
+ tetheringManager.unregisterTetheringEventCallback(mTetherListener);
mContext.unregisterReceiver(mPollReceiver);
mContext.unregisterReceiver(mRemovedReceiver);
mContext.unregisterReceiver(mUserReceiver);
@@ -1150,14 +1154,15 @@
}
/**
- * Receiver that watches for {@link Tethering} to claim interface pairs.
+ * Listener that watches for {@link TetheringManager} to claim interface pairs.
*/
- private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- performPoll(FLAG_PERSIST_NETWORK);
- }
- };
+ private final TetheringManager.TetheringEventCallback mTetherListener =
+ new TetheringManager.TetheringEventCallback() {
+ @Override
+ public void onUpstreamChanged(@Nullable Network network) {
+ performPoll(FLAG_PERSIST_NETWORK);
+ }
+ };
private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
@Override
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 93d0ae7..9bb7bb8 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -101,7 +101,8 @@
// with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
final List<Pair<Integer, String>> filteredNewSubs = new ArrayList<>();
for (final int subId : newSubs) {
- final String subscriberId = mTeleManager.getSubscriberId(subId);
+ final String subscriberId =
+ mTeleManager.createForSubscriptionId(subId).getSubscriberId();
if (!TextUtils.isEmpty(subscriberId)) {
filteredNewSubs.add(new Pair(subId, subscriberId));
}
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
index e586dbb..b127630 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -24,7 +24,6 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical"
- android:importantForAccessibility = "no"
android:clipToPadding="false">
<LinearLayout
diff --git a/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml
index 990860a..23192b6 100644
--- a/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml
@@ -23,7 +23,6 @@
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
- android:importantForAccessibility = "no"
android:clipToPadding="false">
<LinearLayout
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index fe7988f..e51bb45 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -61,6 +61,7 @@
title.setMovementMethod(new LinkMovementMethod());
title.setClickable(false);
title.setLongClickable(false);
+ title.setFocusable(false);
if (!TextUtils.isEmpty(mContentDescription)) {
title.setContentDescription(mContentDescription);
}
@@ -79,6 +80,7 @@
if (!TextUtils.isEmpty(mLearnMoreContentDescription)) {
learnMore.setContentDescription(mLearnMoreContentDescription);
}
+ learnMore.setFocusable(false);
} else {
learnMore.setVisibility(View.GONE);
}
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index c439cf0..9bccc3f 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -22,7 +22,7 @@
<!-- The translation for disappearing security views after having solved them. -->
<dimen name="disappear_y_translation">-32dp</dimen>
- <dimen name="circle_avatar_size">40dp</dimen>
+ <dimen name="circle_avatar_size">190dp</dimen>
<!-- Height of a user icon view -->
<dimen name="user_icon_view_height">24dp</dimen>
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index ab7b54d..6b9b750 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -17,9 +17,11 @@
package com.android.settingslib.dream;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -57,6 +59,8 @@
public boolean isActive;
public ComponentName componentName;
public ComponentName settingsComponentName;
+ public CharSequence description;
+ public Drawable previewImage;
@Override
public String toString() {
@@ -118,23 +122,47 @@
PackageManager.GET_META_DATA);
List<DreamInfo> dreamInfos = new ArrayList<>(resolveInfos.size());
for (ResolveInfo resolveInfo : resolveInfos) {
- if (resolveInfo.serviceInfo == null)
+ if (resolveInfo.serviceInfo == null) {
continue;
+ }
DreamInfo dreamInfo = new DreamInfo();
dreamInfo.caption = resolveInfo.loadLabel(pm);
dreamInfo.icon = resolveInfo.loadIcon(pm);
+ dreamInfo.description = getDescription(resolveInfo, pm);
dreamInfo.componentName = getDreamComponentName(resolveInfo);
dreamInfo.isActive = dreamInfo.componentName.equals(activeDream);
- dreamInfo.settingsComponentName = getSettingsComponentName(pm, resolveInfo);
+
+ final DreamMetadata dreamMetadata = getDreamMetadata(pm, resolveInfo);
+ if (dreamMetadata != null) {
+ dreamInfo.settingsComponentName = dreamMetadata.mSettingsActivity;
+ dreamInfo.previewImage = dreamMetadata.mPreviewImage;
+ }
+
dreamInfos.add(dreamInfo);
}
Collections.sort(dreamInfos, mComparator);
return dreamInfos;
}
+ private static CharSequence getDescription(ResolveInfo resolveInfo, PackageManager pm) {
+ String packageName = resolveInfo.resolvePackageName;
+ ApplicationInfo applicationInfo = null;
+ if (packageName == null) {
+ packageName = resolveInfo.serviceInfo.packageName;
+ applicationInfo = resolveInfo.serviceInfo.applicationInfo;
+ }
+ if (resolveInfo.serviceInfo.descriptionRes != 0) {
+ return pm.getText(packageName,
+ resolveInfo.serviceInfo.descriptionRes,
+ applicationInfo);
+ }
+ return null;
+ }
+
public ComponentName getDefaultDream() {
- if (mDreamManager == null)
+ if (mDreamManager == null) {
return null;
+ }
try {
return mDreamManager.getDefaultDreamComponentForUser(mContext.getUserId());
} catch (RemoteException e) {
@@ -306,57 +334,77 @@
}
private static ComponentName getDreamComponentName(ResolveInfo resolveInfo) {
- if (resolveInfo == null || resolveInfo.serviceInfo == null)
+ if (resolveInfo == null || resolveInfo.serviceInfo == null) {
return null;
+ }
return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
}
- private static ComponentName getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo) {
- if (resolveInfo == null
- || resolveInfo.serviceInfo == null
- || resolveInfo.serviceInfo.metaData == null)
+ private static final class DreamMetadata {
+ @Nullable
+ Drawable mPreviewImage;
+ @Nullable
+ ComponentName mSettingsActivity;
+ }
+
+ @Nullable
+ private static TypedArray readMetadata(PackageManager pm, ServiceInfo serviceInfo) {
+ if (serviceInfo == null || serviceInfo.metaData == null) {
return null;
- String cn = null;
- XmlResourceParser parser = null;
- Exception caughtException = null;
- try {
- parser = resolveInfo.serviceInfo.loadXmlMetaData(pm, DreamService.DREAM_META_DATA);
+ }
+ try (XmlResourceParser parser =
+ serviceInfo.loadXmlMetaData(pm, DreamService.DREAM_META_DATA)) {
if (parser == null) {
Log.w(TAG, "No " + DreamService.DREAM_META_DATA + " meta-data");
return null;
}
- Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
+ Resources res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
AttributeSet attrs = Xml.asAttributeSet(parser);
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && type != XmlPullParser.START_TAG) {
+ while (true) {
+ final int type = parser.next();
+ if (type == XmlPullParser.END_DOCUMENT || type == XmlPullParser.START_TAG) {
+ break;
+ }
}
String nodeName = parser.getName();
if (!"dream".equals(nodeName)) {
Log.w(TAG, "Meta-data does not start with dream tag");
return null;
}
- TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.Dream);
- cn = sa.getString(com.android.internal.R.styleable.Dream_settingsActivity);
- sa.recycle();
- } catch (PackageManager.NameNotFoundException|IOException|XmlPullParserException e) {
- caughtException = e;
- } finally {
- if (parser != null) parser.close();
- }
- if (caughtException != null) {
- Log.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
+ return res.obtainAttributes(attrs, com.android.internal.R.styleable.Dream);
+ } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
+ Log.w(TAG, "Error parsing : " + serviceInfo.packageName, e);
return null;
}
- if (cn != null && cn.indexOf('/') < 0) {
- cn = resolveInfo.serviceInfo.packageName + "/" + cn;
+ }
+
+ private static ComponentName convertToComponentName(String flattenedString,
+ ServiceInfo serviceInfo) {
+ if (flattenedString == null) return null;
+
+ if (flattenedString.indexOf('/') < 0) {
+ flattenedString = serviceInfo.packageName + "/" + flattenedString;
}
- return cn == null ? null : ComponentName.unflattenFromString(cn);
+ return ComponentName.unflattenFromString(flattenedString);
+ }
+
+ private static DreamMetadata getDreamMetadata(PackageManager pm, ResolveInfo resolveInfo) {
+ DreamMetadata result = new DreamMetadata();
+ if (resolveInfo == null) return result;
+ TypedArray rawMetadata = readMetadata(pm, resolveInfo.serviceInfo);
+ if (rawMetadata == null) return result;
+ result.mSettingsActivity = convertToComponentName(rawMetadata.getString(
+ com.android.internal.R.styleable.Dream_settingsActivity), resolveInfo.serviceInfo);
+ result.mPreviewImage = rawMetadata.getDrawable(
+ com.android.internal.R.styleable.Dream_previewImage);
+ rawMetadata.recycle();
+ return result;
}
private static void logd(String msg, Object... args) {
- if (DEBUG)
+ if (DEBUG) {
Log.d(TAG, args == null || args.length == 0 ? msg : String.format(msg, args));
+ }
}
private static class DreamInfoComparator implements Comparator<DreamInfo> {
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 4e2111c..16cece9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -188,6 +188,7 @@
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
+ Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
Settings.Secure.NOTIFICATION_BUBBLES,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index dd1cb6b..13c1e51 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -229,6 +229,7 @@
VALIDATORS.put(Secure.SKIP_DIRECTION, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SILENCE_GESTURE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, JSON_OBJECT_VALIDATOR);
+ VALIDATORS.put(Secure.NAV_BAR_KIDS_MODE, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.NAVIGATION_MODE, new DiscreteValueValidator(new String[] {"0", "1", "2"}));
VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_LEFT,
@@ -321,5 +322,7 @@
}
return true;
});
+ VALIDATORS.put(Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED, BOOLEAN_VALIDATOR);
+
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 38a258f..cd6447f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -244,9 +244,6 @@
final long autofillToken = p.start(GlobalSettingsProto.AUTOFILL);
dumpSetting(s, p,
- Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
- GlobalSettingsProto.Autofill.COMPAT_MODE_ALLOWED_PACKAGES);
- dumpSetting(s, p,
Settings.Global.AUTOFILL_LOGGING_LEVEL,
GlobalSettingsProto.Autofill.LOGGING_LEVEL);
dumpSetting(s, p,
@@ -1813,6 +1810,9 @@
dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
SecureSettingsProto.Accessibility.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
+ SecureSettingsProto.Accessibility.ODI_CAPTIONS_VOLUME_UI_ENABLED);
p.end(accessibilityToken);
final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
@@ -2244,6 +2244,10 @@
SecureSettingsProto.MULTI_PRESS_TIMEOUT);
dumpSetting(s, p,
+ Settings.Secure.NAV_BAR_KIDS_MODE,
+ SecureSettingsProto.NAV_BAR_KIDS_MODE);
+
+ dumpSetting(s, p,
Settings.Secure.NAVIGATION_MODE,
SecureSettingsProto.NAVIGATION_MODE);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 368dda1..a3f3995 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -134,7 +134,6 @@
Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE,
Settings.Global.ASSISTED_GPS_ENABLED,
Settings.Global.AUDIO_SAFE_VOLUME_STATE,
- Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
Settings.Global.AUTOFILL_LOGGING_LEVEL,
Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 559c31a..b19ef3a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -234,15 +234,23 @@
plugins: ["dagger2-compiler"],
}
-soong_config_module_type_import {
- from: "frameworks/base/services/Android.bp",
- module_types: ["system_optimized_java_defaults"],
+// Opt-in config for optimizing the SystemUI target using R8.
+// Enabled via `export SYSTEMUI_OPTIMIZE_JAVA=true`, or explicitly in Make via
+// the `SOONG_CONFIG_ANDROID_SYSTEMUI_OPTIMIZE_JAVA` variable.
+// TODO(b/203472868): Enable optimizations by default after stabilizing and
+// building out retrace infrastructure.
+soong_config_module_type {
+ name: "systemui_optimized_java_defaults",
+ module_type: "java_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: ["SYSTEMUI_OPTIMIZE_JAVA"],
+ properties: ["optimize"],
}
-system_optimized_java_defaults {
+systemui_optimized_java_defaults {
name: "SystemUI_app_defaults",
soong_config_variables: {
- SYSTEM_OPTIMIZE_JAVA: {
+ SYSTEMUI_OPTIMIZE_JAVA: {
optimize: {
enabled: true,
optimize: true,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1e9a41e..e907efb 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -260,6 +260,8 @@
<uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<!-- For handling silent audio recordings -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
+ <!-- For asking AudioManager audio information -->
+ <uses-permission android:name="android.permission.QUERY_AUDIO_STATE"/>
<!-- to read and change hvac values in a car -->
<uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
diff --git a/packages/SystemUI/docs/qs-tiles.md b/packages/SystemUI/docs/qs-tiles.md
index efcb2de..4cb765d 100644
--- a/packages/SystemUI/docs/qs-tiles.md
+++ b/packages/SystemUI/docs/qs-tiles.md
@@ -357,10 +357,12 @@
Updates the `State` of the Tile based on the state of the device as provided by the respective controller. It will be called every time the Tile becomes visible, is interacted with or `QSTileImpl#refreshState` is called. After this is done, the updated state will be reflected in the UI.
* ```java
+ @Deprecated
public int getMetricsCategory()
```
- Identifier for this Tile, as defined in [proto/src/metrics_constants/metrics_constants.proto](/proto/src/metrics_constants/metrics_constants.proto). This is used to log events related to this Tile.
+ ~~Identifier for this Tile, as defined in [proto/src/metrics_constants/metrics_constants.proto](/proto/src/metrics_constants/metrics_constants.proto). This is used to log events related to this Tile.~~
+ This is now deprecated in favor of `UiEvent` that use the tile spec.
* ```java
public boolean isAvailable()
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index d5f858c..8ad2009 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -34,7 +34,7 @@
String ACTION = "com.android.systemui.action.PLUGIN_QS";
- int VERSION = 12;
+ int VERSION = 13;
String TAG = "QS";
@@ -70,6 +70,11 @@
void setContainerController(QSContainerController controller);
void setExpandClickListener(OnClickListener onClickListener);
+ /**
+ * Returns the height difference between the QSPanel container and the QuickQSPanel container
+ */
+ int getHeightDiff();
+
View getHeader();
default void setHasNotifications(boolean hasNotifications) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 77018d7..ffac26b 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -40,7 +40,7 @@
@DependsOn(target = Icon.class)
@DependsOn(target = State.class)
public interface QSTile {
- int VERSION = 1;
+ int VERSION = 2;
DetailAdapter getDetailAdapter();
String getTileSpec();
@@ -79,6 +79,12 @@
void longClick(@Nullable View view);
void userSwitch(int currentUser);
+
+ /**
+ * @deprecated not needed as {@link com.android.internal.logging.UiEvent} will use
+ * {@link #getMetricsSpec}
+ */
+ @Deprecated
int getMetricsCategory();
void setListening(Object client, boolean listening);
@@ -117,7 +123,6 @@
void onShowDetail(boolean show);
void onToggleStateChanged(boolean state);
void onScanStateChanged(boolean state);
- void onAnnouncementRequested(CharSequence announcement);
}
@ProvidesInterface(version = Icon.VERSION)
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
deleted file mode 100644
index 384e02d..0000000
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/transparent"
- android:clipChildren="false"
- android:clipToPadding="false">
-
- <include
- layout="@layout/keyguard_host_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-</FrameLayout>
-
diff --git a/packages/SystemUI/res/drawable/ic_qs_color_correction.xml b/packages/SystemUI/res/drawable/ic_qs_color_correction.xml
new file mode 100644
index 0000000..f83cabd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_color_correction.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2022 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M3,21v-4.75l8.95,-8.95 -1.45,-1.4 1.45,-1.4 1.9,1.9 3.1,-3.1q0.275,-0.275 0.7,-0.275 0.425,0 0.7,0.275l2.35,2.35q0.275,0.275 0.275,0.7 0,0.425 -0.275,0.7l-3.075,3.075 1.9,1.95L18.1,13.5l-1.4,-1.45L7.75,21zM5,19h1.95l8.3,-8.35 -1.9,-1.9L5,17.05z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index 6b054a9..619591d 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -20,4 +20,6 @@
android:id="@+id/qs_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="1"/>
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:clipToPadding="false" />
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index b28cb2f..60860ba 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -101,7 +101,10 @@
<FrameLayout android:id="@+id/keyguard_bouncer_container"
android:layout_height="0dp"
android:layout_width="match_parent"
- android:layout_weight="1" />
+ android:layout_weight="1"
+ android:background="@android:color/transparent"
+ android:clipChildren="false"
+ android:clipToPadding="false" />
</LinearLayout>
<com.android.systemui.biometrics.AuthRippleView
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 2f0957c..2eff692 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -42,6 +42,7 @@
<item>@string/config_systemUIVendorServiceComponent</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
+ <item>com.android.systemui.accessibility.WindowMagnification</item>
<item>com.android.systemui.toast.ToastUI</item>
<item>com.android.systemui.wmshell.WMShell</item>
<item>com.android.systemui.media.systemsounds.HomeSoundEffectController</item>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9f017b2..7b8f349 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -82,7 +82,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,fgsmanager
+ internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,fgsmanager,color_correction
</string>
<!-- The tiles to display in QuickSettings -->
@@ -97,6 +97,7 @@
The syntax is setting-name:spec. If the tile is a TileService, the spec should be specified
as custom(package/class). Relative class name is supported. -->
<string-array name="config_quickSettingsAutoAdd" translatable="false">
+ <item>accessibility_display_daltonizer_enabled:color_correction</item>
<item>accessibility_display_inversion_enabled:inversion</item>
<item>one_handed_mode_enabled:onehanded</item>
</string-array>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8482044..2bf121d2e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -425,60 +425,24 @@
<!-- Content description for the close button in the zen mode panel introduction message. [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_close">Close</string>
- <!-- Announcement made when the wifi is turned off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_wifi_changed_off">Wifi turned off.</string>
- <!-- Announcement made when the wifi is turned on (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_wifi_changed_on">Wifi turned on.</string>
- <!-- Announcement made when the airplane mode changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_airplane_changed_off">Airplane mode turned off.</string>
- <!-- Announcement made when the airplane mode changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_airplane_changed_on">Airplane mode turned on.</string>
<!-- Content description of the do not disturb tile in quick settings when on in none (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_dnd_none_on">total silence</string>
<!-- Content description of the do not disturb tile in quick settings when on in alarms only (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_dnd_alarms_on">alarms only</string>
<!-- Content description of the do not disturb tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_dnd">Do Not Disturb.</string>
- <!-- Announcement made when do not disturb changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_dnd_changed_off">Do Not Disturb turned off.</string>
- <!-- Announcement made when do not disturb changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_dnd_changed_on">Do Not Disturb turned on.</string>
<!-- Content description of the bluetooth tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_bluetooth">Bluetooth.</string>
<!-- Content description of the bluetooth tile in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_bluetooth_on">Bluetooth on.</string>
- <!-- Announcement made when the bluetooth is turned off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_bluetooth_changed_off">Bluetooth turned off.</string>
- <!-- Announcement made when the bluetooth is turned on (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_bluetooth_changed_on">Bluetooth turned on.</string>
- <!-- Announcement made when the location tile changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_location_changed_off">Location reporting turned off.</string>
- <!-- Announcement made when the location tile changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_location_changed_on">Location reporting turned on.</string>
<!-- Content description of the alarm tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_alarm">Alarm set for <xliff:g id="time" example="Wed 3:30 PM">%s</xliff:g>.</string>
<!-- Content description of zen mode time condition plus button (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_more_time">More time.</string>
<!-- Content description of zen mode time condition minus button (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_less_time">Less time.</string>
- <!-- Announcement made when the flashlight state changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_flashlight_changed_off">Flashlight turned off.</string>
- <!-- Announcement made when the flashlight state changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_flashlight_changed_on">Flashlight turned on.</string>
- <!-- Announcement made when the hotspot state changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_hotspot_changed_off">Mobile hotspot turned off.</string>
- <!-- Announcement made when the hotspot state changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_hotspot_changed_on">Mobile hotspot turned on.</string>
<!-- Announcement made when the screen stopped casting (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_casting_turned_off">Screen casting stopped.</string>
- <!-- Announcement made when the work mode changes to off (not shown on the screen). Paused is used as a verb. [CHAR LIMIT=NONE] -->
- <!-- Announcement made when the work mode changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
- <!-- Announcement made when the Data Saver changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_data_saver_changed_off">Data Saver turned off.</string>
- <!-- Announcement made when the Data Saver changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_data_saver_changed_on">Data Saver turned on.</string>
- <!-- Announcement made when the Sensor Privacy changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <!-- Announcement made when the Sensor Privacy changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
<!-- Content description of the display brightness slider (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_brightness">Display brightness</string>
@@ -632,6 +596,7 @@
<!-- QuickSettings: Label for the toggle that controls whether display inversion is enabled. [CHAR LIMIT=NONE] -->
<string name="quick_settings_inversion_label">Color inversion</string>
<!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_color_correction_label">Color correction</string>
<!-- QuickSettings: Control panel: Label for button that navigates to settings. [CHAR LIMIT=NONE] -->
<string name="quick_settings_more_settings">More settings</string>
<!-- QuickSettings: Control panel: Label for button that navigates to user settings. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml
index 5fdb497..a610caa 100644
--- a/packages/SystemUI/res/values/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values/tiles_states_strings.xml
@@ -142,6 +142,16 @@
<item>On</item>
</string-array>
+ <!-- State names for color correction tile: unavailable, off, on.
+ This subtitle is shown when the tile is in that particular state but does not set its own
+ subtitle, so some of these may never appear on screen. They should still be translated as
+ if they could appear. [CHAR LIMIT=32] -->
+ <string-array name="tile_states_color_correction">
+ <item>Unavailable</item>
+ <item>Off</item>
+ <item>On</item>
+ </string-array>
+
<!-- State names for (color) inversion tile: unavailable, off, on.
This subtitle is shown when the tile is in that particular state but does not set its own
subtitle, so some of these may never appear on screen. They should still be translated as
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index c1d9d0d..4ec65d8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -282,7 +282,9 @@
* @see SurfaceControl#release()
*/
public void release() {
- leash.mSurfaceControl.release();
+ if (leash.mSurfaceControl != null) {
+ leash.mSurfaceControl.release();
+ }
if (mStartLeash != null) {
mStartLeash.release();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index ac946ca..24e93ef 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -30,6 +30,7 @@
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
+import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
import java.lang.IllegalStateException
import java.util.concurrent.Executor
@@ -46,7 +47,8 @@
deviceStateManager: DeviceStateManager,
sensorManager: SensorManager,
mainHandler: Handler,
- mainExecutor: Executor
+ mainExecutor: Executor,
+ tracingTagPrefix: String
): UnfoldTransitionProgressProvider {
if (!config.isEnabled) {
@@ -76,9 +78,12 @@
FixedTimingTransitionProgressProvider(foldStateProvider)
}
return ScaleAwareTransitionProgressProvider(
- unfoldTransitionProgressProvider,
- context.contentResolver
- )
+ unfoldTransitionProgressProvider,
+ context.contentResolver
+ ).apply {
+ // Always present callback that logs animation beginning and end.
+ addCallback(ATraceLoggerTransitionProgressListener(tracingTagPrefix))
+ }
}
fun createConfig(context: Context): UnfoldTransitionConfig =
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index 51eae57..a701b44 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -23,7 +23,7 @@
import androidx.dynamicanimation.animation.SpringForce
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.systemui.unfold.updates.FOLD_UPDATE_ABORTED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
@@ -32,12 +32,7 @@
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
-/**
- * Maps fold updates to unfold transition progress using DynamicAnimation.
- *
- * TODO(b/193793338) Current limitations:
- * - doesn't handle postures
- */
+/** Maps fold updates to unfold transition progress using DynamicAnimation. */
internal class PhysicsBasedUnfoldTransitionProgressProvider(
private val foldStateProvider: FoldStateProvider
) :
@@ -89,7 +84,7 @@
cancelTransition(endValue = 1f, animate = true)
}
}
- FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_ABORTED -> {
+ FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_HALF_OPEN -> {
// Do not cancel if we haven't started the transition yet.
// This could happen when we fully unfolded the device before the screen
// became available. In this case we start and immediately cancel the animation
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 6d9631c..cd1ea21 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -138,7 +138,7 @@
if (isTransitionInProgess) {
cancelTimeout()
}
- handler.postDelayed(timeoutRunnable, ABORT_CLOSING_MILLIS)
+ handler.postDelayed(timeoutRunnable, HALF_OPENED_TIMEOUT_MILLIS)
}
private fun cancelTimeout() {
@@ -163,16 +163,14 @@
}
private inner class HingeAngleListener : Consumer<Float> {
-
override fun accept(angle: Float) {
onHingeAngle(angle)
}
}
private inner class TimeoutRunnable : Runnable {
-
override fun run() {
- notifyFoldUpdate(FOLD_UPDATE_ABORTED)
+ notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
}
}
}
@@ -180,9 +178,7 @@
private fun stateToString(@FoldUpdate update: Int): String {
return when (update) {
FOLD_UPDATE_START_OPENING -> "START_OPENING"
- FOLD_UPDATE_HALF_OPEN -> "HALF_OPEN"
FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
- FOLD_UPDATE_ABORTED -> "ABORTED"
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE"
FOLD_UPDATE_FINISH_HALF_OPEN -> "FINISH_HALF_OPEN"
FOLD_UPDATE_FINISH_FULL_OPEN -> "FINISH_FULL_OPEN"
@@ -195,11 +191,11 @@
private const val DEBUG = false
/**
- * Time after which [FOLD_UPDATE_ABORTED] is emitted following a [FOLD_UPDATE_START_CLOSING] or
- * [FOLD_UPDATE_START_OPENING] event, if an end state is not reached.
+ * Time after which [FOLD_UPDATE_FINISH_HALF_OPEN] is emitted following a
+ * [FOLD_UPDATE_START_CLOSING] or [FOLD_UPDATE_START_OPENING] event, if an end state is not reached.
*/
@VisibleForTesting
-const val ABORT_CLOSING_MILLIS = 1000L
+const val HALF_OPENED_TIMEOUT_MILLIS = 1000L
/** Threshold after which we consider the device fully unfolded. */
@VisibleForTesting
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index bffebcd..df3563d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -37,9 +37,7 @@
@IntDef(prefix = ["FOLD_UPDATE_"], value = [
FOLD_UPDATE_START_OPENING,
- FOLD_UPDATE_HALF_OPEN,
FOLD_UPDATE_START_CLOSING,
- FOLD_UPDATE_ABORTED,
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
FOLD_UPDATE_FINISH_HALF_OPEN,
FOLD_UPDATE_FINISH_FULL_OPEN,
@@ -50,10 +48,8 @@
}
const val FOLD_UPDATE_START_OPENING = 0
-const val FOLD_UPDATE_HALF_OPEN = 1
-const val FOLD_UPDATE_START_CLOSING = 2
-const val FOLD_UPDATE_ABORTED = 3
-const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 4
-const val FOLD_UPDATE_FINISH_HALF_OPEN = 5
-const val FOLD_UPDATE_FINISH_FULL_OPEN = 6
-const val FOLD_UPDATE_FINISH_CLOSED = 7
+const val FOLD_UPDATE_START_CLOSING = 1
+const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 2
+const val FOLD_UPDATE_FINISH_HALF_OPEN = 3
+const val FOLD_UPDATE_FINISH_FULL_OPEN = 4
+const val FOLD_UPDATE_FINISH_CLOSED = 5
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt
new file mode 100644
index 0000000..f3eeb32
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt
@@ -0,0 +1,29 @@
+package com.android.systemui.unfold.util
+
+import android.os.Trace
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+
+/**
+ * Listener that logs start and end of the fold-unfold transition.
+ *
+ * [tracePrefix] arg helps in differentiating those. Currently, this is expected to be logged twice
+ * for each fold/unfold: in (1) systemui and (2) launcher process.
+ */
+class ATraceLoggerTransitionProgressListener(tracePrefix: String) : TransitionProgressListener {
+
+ private val traceName = "$tracePrefix#$UNFOLD_TRANSITION_TRACE_NAME"
+
+ override fun onTransitionStarted() {
+ Trace.beginAsyncSection(traceName, /* cookie= */ 0)
+ }
+
+ override fun onTransitionFinished() {
+ Trace.endAsyncSection(traceName, /* cookie= */ 0)
+ }
+
+ override fun onTransitionProgress(progress: Float) {
+ Trace.setCounter(traceName, (progress * 100).toLong())
+ }
+}
+
+private const val UNFOLD_TRANSITION_TRACE_NAME = "FoldUnfoldTransitionInProgress"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java
deleted file mode 100644
index 4e375c2..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.view.ViewGroup;
-
-import com.android.keyguard.dagger.KeyguardBouncerScope;
-import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.util.ViewController;
-
-import javax.inject.Inject;
-/** Controller for a {@link KeyguardBouncer}'s Root view. */
-@KeyguardBouncerScope
-public class KeyguardRootViewController extends ViewController<ViewGroup> {
- @Inject
- public KeyguardRootViewController(@RootView ViewGroup view) {
- super(view);
- }
-
- public ViewGroup getView() {
- return mView;
- }
-
- @Override
- protected void onViewAttached() {
-
- }
-
- @Override
- protected void onViewDetached() {
-
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 2789e27..98721fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -94,7 +94,6 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
-import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
@@ -173,7 +172,6 @@
private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 328;
private static final int MSG_AIRPLANE_MODE_CHANGED = 329;
private static final int MSG_SERVICE_STATE_CHANGE = 330;
- private static final int MSG_SCREEN_TURNED_ON = 331;
private static final int MSG_SCREEN_TURNED_OFF = 332;
private static final int MSG_DREAMING_STATE_CHANGED = 333;
private static final int MSG_USER_UNLOCKED = 334;
@@ -310,7 +308,6 @@
private boolean mSwitchingUser;
private boolean mDeviceInteractive;
- private boolean mScreenOn;
private SubscriptionManager mSubscriptionManager;
private final TelephonyListenerManager mTelephonyListenerManager;
private List<SubscriptionInfo> mSubscriptionInfo;
@@ -1300,10 +1297,6 @@
}
}
- public boolean isScreenOn() {
- return mScreenOn;
- }
-
private void dispatchErrorMessage(CharSequence message) {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1692,29 +1685,10 @@
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
- private void handleScreenTurnedOn() {
- Assert.isMainThread();
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onScreenTurnedOn();
- }
- }
- }
-
private void handleScreenTurnedOff() {
- final String tag = "KeyguardUpdateMonitor#handleScreenTurnedOff";
- DejankUtils.startDetectingBlockingIpcs(tag);
Assert.isMainThread();
mHardwareFingerprintUnavailableRetryCount = 0;
mHardwareFaceUnavailableRetryCount = 0;
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onScreenTurnedOff();
- }
- }
- DejankUtils.stopDetectingBlockingIpcs(tag);
}
private void handleDreamingStateChanged(int dreamStart) {
@@ -1894,11 +1868,8 @@
case MSG_SERVICE_STATE_CHANGE:
handleServiceStateChange(msg.arg1, (ServiceState) msg.obj);
break;
- case MSG_SCREEN_TURNED_ON:
- handleScreenTurnedOn();
- break;
case MSG_SCREEN_TURNED_OFF:
- Trace.beginSection("KeyguardUpdateMonitor#handler MSG_SCREEN_TURNED_ON");
+ Trace.beginSection("KeyguardUpdateMonitor#handler MSG_SCREEN_TURNED_OFF");
handleScreenTurnedOff();
Trace.endSection();
break;
@@ -3319,17 +3290,7 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_FINISHED_GOING_TO_SLEEP, why, 0));
}
- public void dispatchScreenTurnedOn() {
- synchronized (this) {
- mScreenOn = true;
- }
- mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_ON);
- }
-
public void dispatchScreenTurnedOff() {
- synchronized (this) {
- mScreenOn = false;
- }
mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_OFF);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 8170a81..a74fd15 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -201,22 +201,6 @@
public void onFinishedGoingToSleep(int why) { }
/**
- * Called when the screen has been turned on.
- *
- * @deprecated use {@link com.android.systemui.keyguard.ScreenLifecycle}.
- */
- @Deprecated
- public void onScreenTurnedOn() { }
-
- /**
- * Called when the screen has been turned off.
- *
- * @deprecated use {@link com.android.systemui.keyguard.ScreenLifecycle}.
- */
- @Deprecated
- public void onScreenTurnedOff() { }
-
- /**
* Called when trust changes for a user.
*/
public void onTrustChanged(int userId) { }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index fcf1b2c..122f3d7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -70,16 +70,6 @@
default void onStartedWakingUp() {};
/**
- * Called when the device started turning on.
- */
- default void onScreenTurningOn() {};
-
- /**
- * Called when the device has finished turning on.
- */
- default void onScreenTurnedOn() {};
-
- /**
* Sets whether the Keyguard needs input.
* @param needsInput
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
index 5160b7e..0cbf8bc 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
@@ -16,10 +16,13 @@
package com.android.keyguard.dagger;
+import android.view.ViewGroup;
+
import com.android.keyguard.KeyguardHostViewController;
-import com.android.keyguard.KeyguardRootViewController;
+import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import dagger.BindsInstance;
import dagger.Subcomponent;
/**
@@ -31,12 +34,9 @@
/** Simple factory for {@link KeyguardBouncerComponent}. */
@Subcomponent.Factory
interface Factory {
- KeyguardBouncerComponent create();
+ KeyguardBouncerComponent create(@BindsInstance @RootView ViewGroup bouncerContainer);
}
- /** Returns a {@link KeyguardRootViewController}. */
- KeyguardRootViewController getKeyguardRootViewController();
-
/** Returns a {@link KeyguardHostViewController}. */
KeyguardHostViewController getKeyguardHostViewController();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index 4fad9a9..b3c1158 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -20,7 +20,6 @@
import android.view.ViewGroup;
import com.android.keyguard.KeyguardHostView;
-import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardSecurityContainer;
import com.android.keyguard.KeyguardSecurityViewFlipper;
import com.android.systemui.R;
@@ -35,26 +34,16 @@
*/
@Module
public interface KeyguardBouncerModule {
- /** */
- @Provides
- @KeyguardBouncerScope
- @RootView
- static ViewGroup providesRootView(LayoutInflater layoutInflater) {
- return (ViewGroup) layoutInflater.inflate(R.layout.keyguard_bouncer, null);
- }
/** */
@Provides
@KeyguardBouncerScope
- static KeyguardMessageArea providesKeyguardMessageArea(@RootView ViewGroup viewGroup) {
- return viewGroup.findViewById(R.id.keyguard_message_area);
- }
-
- /** */
- @Provides
- @KeyguardBouncerScope
- static KeyguardHostView providesKeyguardHostView(@RootView ViewGroup rootView) {
- return rootView.findViewById(R.id.keyguard_host_view);
+ static KeyguardHostView providesKeyguardHostView(@RootView ViewGroup rootView,
+ LayoutInflater layoutInflater) {
+ KeyguardHostView hostView = (KeyguardHostView) layoutInflater.inflate(
+ R.layout.keyguard_host_view, rootView, false);
+ rootView.addView(hostView);
+ return hostView;
}
/** */
diff --git a/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt b/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt
new file mode 100644
index 0000000..705cf6d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 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.keyguard.mediator
+
+import android.os.Trace
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.util.concurrency.Execution
+import com.android.systemui.util.concurrency.PendingTasksContainer
+import com.android.systemui.unfold.SysUIUnfoldComponent
+import com.android.systemui.util.kotlin.getOrNull
+
+import java.util.Optional
+
+import javax.inject.Inject
+
+/**
+ * Coordinates screen on/turning on animations for the KeyguardViewMediator. Specifically for
+ * screen on events, this will invoke the onDrawn Runnable after all tasks have completed. This
+ * should route back to the KeyguardService, which informs the system_server that keyguard has
+ * drawn.
+ */
+@SysUISingleton
+class ScreenOnCoordinator @Inject constructor(
+ screenLifecycle: ScreenLifecycle,
+ unfoldComponent: Optional<SysUIUnfoldComponent>,
+ private val execution: Execution
+) : ScreenLifecycle.Observer {
+
+ private val unfoldLightRevealAnimation = unfoldComponent.map(
+ SysUIUnfoldComponent::getUnfoldLightRevealOverlayAnimation).getOrNull()
+ private val foldAodAnimationController = unfoldComponent.map(
+ SysUIUnfoldComponent::getFoldAodAnimationController).getOrNull()
+ private val pendingTasks = PendingTasksContainer()
+
+ private var wakeAndUnlockingTask: Runnable? = null
+ var wakeAndUnlocking = false
+ set(value) {
+ if (!value && field) {
+ // When updating the value back to false, mark the task complete in order to
+ // callback onDrawn
+ wakeAndUnlockingTask?.run()
+ wakeAndUnlockingTask = null
+ }
+ field = value
+ }
+
+ init {
+ screenLifecycle.addObserver(this)
+ }
+
+ /**
+ * When turning on, registers tasks that may need to run before invoking [onDrawn].
+ */
+ override fun onScreenTurningOn(onDrawn: Runnable) {
+ execution.assertIsMainThread()
+ Trace.beginSection("ScreenOnCoordinator#onScreenTurningOn")
+
+ pendingTasks.reset()
+
+ unfoldLightRevealAnimation?.onScreenTurningOn(pendingTasks.registerTask("unfold-reveal"))
+ foldAodAnimationController?.onScreenTurningOn(pendingTasks.registerTask("fold-to-aod"))
+
+ if (wakeAndUnlocking) {
+ wakeAndUnlockingTask = pendingTasks.registerTask("wake-and-unlocking")
+ }
+
+ pendingTasks.onTasksComplete { onDrawn.run() }
+ Trace.endSection()
+ }
+
+ override fun onScreenTurnedOn() {
+ execution.assertIsMainThread()
+
+ foldAodAnimationController?.onScreenTurnedOn()
+
+ pendingTasks.reset()
+ }
+
+ override fun onScreenTurnedOff() {
+ wakeAndUnlockingTask = null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
index 5019e65..32b58c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
@@ -19,7 +19,10 @@
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
+import android.os.RemoteException;
+import android.util.Log;
+import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.systemui.dagger.SysUISingleton;
import javax.inject.Inject;
@@ -39,6 +42,7 @@
static final int FINISHED_WAKING_UP = 5;
static final int STARTED_GOING_TO_SLEEP = 6;
static final int FINISHED_GOING_TO_SLEEP = 7;
+ private static final String TAG = "KeyguardLifecyclesDispatcher";
private final ScreenLifecycle mScreenLifecycle;
private final WakefulnessLifecycle mWakefulnessLifecycle;
@@ -65,12 +69,38 @@
message.sendToTarget();
}
+ /**
+ * @param what Message to send.
+ * @param object Object to send with the message
+ */
+ void dispatch(int what, Object object) {
+ mHandler.obtainMessage(what, object).sendToTarget();
+ }
+
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
+ final Object obj = msg.obj;
switch (msg.what) {
case SCREEN_TURNING_ON:
- mScreenLifecycle.dispatchScreenTurningOn();
+ // Ensure the drawn callback is only ever called once
+ mScreenLifecycle.dispatchScreenTurningOn(new Runnable() {
+ boolean mInvoked;
+ @Override
+ public void run() {
+ if (obj == null) return;
+ if (!mInvoked) {
+ mInvoked = true;
+ try {
+ ((IKeyguardDrawnCallback) obj).onDrawn();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Exception calling onDrawn():", e);
+ }
+ } else {
+ Log.w(TAG, "KeyguardDrawnCallback#onDrawn() invoked > 1 times");
+ }
+ }
+ });
break;
case SCREEN_TURNED_ON:
mScreenLifecycle.dispatchScreenTurnedOn();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index fb31d88..e88011e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -500,8 +500,8 @@
public void onScreenTurningOn(IKeyguardDrawnCallback callback) {
Trace.beginSection("KeyguardService.mBinder#onScreenTurningOn");
checkPermission();
- mKeyguardViewMediator.onScreenTurningOn(callback);
- mKeyguardLifecyclesDispatcher.dispatch(KeyguardLifecyclesDispatcher.SCREEN_TURNING_ON);
+ mKeyguardLifecyclesDispatcher.dispatch(KeyguardLifecyclesDispatcher.SCREEN_TURNING_ON,
+ callback);
Trace.endSection();
}
@@ -509,7 +509,6 @@
public void onScreenTurnedOn() {
Trace.beginSection("KeyguardService.mBinder#onScreenTurnedOn");
checkPermission();
- mKeyguardViewMediator.onScreenTurnedOn();
mKeyguardLifecyclesDispatcher.dispatch(KeyguardLifecyclesDispatcher.SCREEN_TURNED_ON);
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8f4a28c..4658a74 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -87,7 +87,6 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardStateCallback;
import com.android.internal.util.LatencyTracker;
@@ -99,6 +98,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.Interpolators;
@@ -122,15 +122,11 @@
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.unfold.FoldAodAnimationController;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
-import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.util.DeviceConfigProxy;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Optional;
import java.util.concurrent.Executor;
import dagger.Lazy;
@@ -146,7 +142,7 @@
* so that once the screen comes on, it will be ready immediately.
*
* Example queries about the keyguard:
- * - is {movement, key} one that should wake the keygaurd?
+ * - is {movement, key} one that should wake the keyguard?
* - is the keyguard showing?
* - are input events restricted due to the state of the keyguard?
*
@@ -199,7 +195,6 @@
private static final int RESET = 3;
private static final int VERIFY_UNLOCK = 4;
private static final int NOTIFY_FINISHED_GOING_TO_SLEEP = 5;
- private static final int NOTIFY_SCREEN_TURNING_ON = 6;
private static final int KEYGUARD_DONE = 7;
private static final int KEYGUARD_DONE_DRAWING = 8;
private static final int SET_OCCLUDED = 9;
@@ -208,8 +203,6 @@
private static final int START_KEYGUARD_EXIT_ANIM = 12;
private static final int KEYGUARD_DONE_PENDING_TIMEOUT = 13;
private static final int NOTIFY_STARTED_WAKING_UP = 14;
- private static final int NOTIFY_SCREEN_TURNED_ON = 15;
- private static final int NOTIFY_SCREEN_TURNED_OFF = 16;
private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 17;
private static final int SYSTEM_READY = 18;
private static final int CANCEL_KEYGUARD_EXIT_ANIM = 19;
@@ -429,17 +422,10 @@
*/
private WorkLockActivityController mWorkLockController;
- /**
- * @see #setPulsing(boolean)
- */
- private boolean mPulsing;
-
private boolean mLockLater;
private boolean mShowHomeOverLockscreen;
private boolean mInGestureNavigationMode;
- private boolean mWakeAndUnlocking;
- private Runnable mWakeAndUnlockingDrawnCallback;
private CharSequence mCustomMessage;
/**
@@ -816,14 +802,11 @@
private DeviceConfigProxy mDeviceConfig;
private DozeParameters mDozeParameters;
- private final Optional<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealAnimation;
- private final Optional<FoldAodAnimationController> mFoldAodAnimationController;
- private final PendingDrawnTasksContainer mPendingDrawnTasks = new PendingDrawnTasksContainer();
-
private final KeyguardStateController mKeyguardStateController;
private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
private final InteractionJankMonitor mInteractionJankMonitor;
private boolean mWallpaperSupportsAmbientMode;
+ private ScreenOnCoordinator mScreenOnCoordinator;
/**
* Injected constructor. See {@link KeyguardModule}.
@@ -843,12 +826,12 @@
NavigationModeController navigationModeController,
KeyguardDisplayManager keyguardDisplayManager,
DozeParameters dozeParameters,
- Optional<SysUIUnfoldComponent> unfoldComponent,
SysuiStatusBarStateController statusBarStateController,
KeyguardStateController keyguardStateController,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationControllerLazy,
ScreenOffAnimationController screenOffAnimationController,
Lazy<NotificationShadeDepthController> notificationShadeDepthController,
+ ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor) {
super(context);
mFalsingCollector = falsingCollector;
@@ -865,6 +848,7 @@
mKeyguardDisplayManager = keyguardDisplayManager;
dumpManager.registerDumpable(getClass().getName(), this);
mDeviceConfig = deviceConfig;
+ mScreenOnCoordinator = screenOnCoordinator;
mShowHomeOverLockscreen = mDeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
@@ -879,11 +863,6 @@
}));
mDozeParameters = dozeParameters;
- mUnfoldLightRevealAnimation = unfoldComponent
- .map(SysUIUnfoldComponent::getUnfoldLightRevealOverlayAnimation);
- mFoldAodAnimationController = unfoldComponent
- .map(SysUIUnfoldComponent::getFoldAodAnimationController);
-
mStatusBarStateController = statusBarStateController;
statusBarStateController.addCallback(this);
@@ -1076,7 +1055,7 @@
synchronized (this) {
mDeviceInteractive = false;
mGoingToSleep = false;
- mWakeAndUnlocking = false;
+ mScreenOnCoordinator.setWakeAndUnlocking(false);
mAnimatingScreenOff = mDozeParameters.shouldAnimateDozingChange();
resetKeyguardDonePendingLocked();
@@ -1254,21 +1233,7 @@
Trace.endSection();
}
- public void onScreenTurningOn(IKeyguardDrawnCallback callback) {
- Trace.beginSection("KeyguardViewMediator#onScreenTurningOn");
- notifyScreenOn(callback);
- Trace.endSection();
- }
-
- public void onScreenTurnedOn() {
- Trace.beginSection("KeyguardViewMediator#onScreenTurnedOn");
- notifyScreenTurnedOn();
- mUpdateMonitor.dispatchScreenTurnedOn();
- Trace.endSection();
- }
-
public void onScreenTurnedOff() {
- notifyScreenTurnedOff();
mUpdateMonitor.dispatchScreenTurnedOff();
}
@@ -1283,7 +1248,7 @@
// Skipping the lockscreen because we're not yet provisioned, but we still need to
// notify the StrongAuthTracker that it's now safe to run trust agents, in case the
// user sets a credential later.
- getLockPatternUtils().userPresent(KeyguardUpdateMonitor.getCurrentUser());
+ mLockPatternUtils.userPresent(KeyguardUpdateMonitor.getCurrentUser());
}
}
@@ -1660,24 +1625,6 @@
mHandler.sendEmptyMessage(NOTIFY_STARTED_WAKING_UP);
}
- private void notifyScreenOn(IKeyguardDrawnCallback callback) {
- if (DEBUG) Log.d(TAG, "notifyScreenOn");
- Message msg = mHandler.obtainMessage(NOTIFY_SCREEN_TURNING_ON, callback);
- mHandler.sendMessage(msg);
- }
-
- private void notifyScreenTurnedOn() {
- if (DEBUG) Log.d(TAG, "notifyScreenTurnedOn");
- Message msg = mHandler.obtainMessage(NOTIFY_SCREEN_TURNED_ON);
- mHandler.sendMessage(msg);
- }
-
- private void notifyScreenTurnedOff() {
- if (DEBUG) Log.d(TAG, "notifyScreenTurnedOff");
- Message msg = mHandler.obtainMessage(NOTIFY_SCREEN_TURNED_OFF);
- mHandler.sendMessage(msg);
- }
-
/**
* Send message to keyguard telling it to show itself
* @see #handleShow
@@ -1838,21 +1785,6 @@
case NOTIFY_FINISHED_GOING_TO_SLEEP:
handleNotifyFinishedGoingToSleep();
break;
- case NOTIFY_SCREEN_TURNING_ON:
- Trace.beginSection(
- "KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNING_ON");
- handleNotifyScreenTurningOn((IKeyguardDrawnCallback) msg.obj);
- Trace.endSection();
- break;
- case NOTIFY_SCREEN_TURNED_ON:
- Trace.beginSection(
- "KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNED_ON");
- handleNotifyScreenTurnedOn();
- Trace.endSection();
- break;
- case NOTIFY_SCREEN_TURNED_OFF:
- handleNotifyScreenTurnedOff();
- break;
case NOTIFY_STARTED_WAKING_UP:
Trace.beginSection(
"KeyguardViewMediator#handleMessage NOTIFY_STARTED_WAKING_UP");
@@ -1984,7 +1916,7 @@
for (int profileId : um.getProfileIdsWithDisabled(currentUser.getIdentifier())) {
mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, UserHandle.of(profileId));
}
- getLockPatternUtils().userPresent(currentUserId);
+ mLockPatternUtils.userPresent(currentUserId);
});
} else {
mBootSendUserPresent = true;
@@ -2081,7 +2013,7 @@
mHiding = false;
mKeyguardExitAnimationRunner = null;
- mWakeAndUnlocking = false;
+ mScreenOnCoordinator.setWakeAndUnlocking(false);
mPendingLock = false;
setShowingLocked(true);
mKeyguardViewControllerLazy.get().show(options);
@@ -2113,12 +2045,14 @@
int flags = 0;
if (mKeyguardViewControllerLazy.get().shouldDisableWindowAnimationsForUnlock()
- || mWakeAndUnlocking && !mWallpaperSupportsAmbientMode) {
+ || mScreenOnCoordinator.getWakeAndUnlocking()
+ && !mWallpaperSupportsAmbientMode) {
flags |= WindowManagerPolicyConstants
.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
}
if (mKeyguardViewControllerLazy.get().isGoingToNotificationShade()
- || mWakeAndUnlocking && mWallpaperSupportsAmbientMode) {
+ || mScreenOnCoordinator.getWakeAndUnlocking()
+ && mWallpaperSupportsAmbientMode) {
// When the wallpaper supports ambient mode, the scrim isn't fully opaque during
// wake and unlock and we should fade in the app on top of the wallpaper
flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
@@ -2229,14 +2163,13 @@
IRemoteAnimationRunner runner = mKeyguardExitAnimationRunner;
mKeyguardExitAnimationRunner = null;
- if (mWakeAndUnlocking && mWakeAndUnlockingDrawnCallback != null) {
+ if (mScreenOnCoordinator.getWakeAndUnlocking()) {
// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
// the next draw from here so we don't have to wait for window manager to signal
// this to our ViewRootImpl.
mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw();
- mWakeAndUnlockingDrawnCallback.run();
- mWakeAndUnlockingDrawnCallback = null;
+ mScreenOnCoordinator.setWakeAndUnlocking(false);
}
LatencyTracker.getInstance(mContext)
@@ -2360,7 +2293,7 @@
}
setShowingLocked(false);
- mWakeAndUnlocking = false;
+ mScreenOnCoordinator.setWakeAndUnlocking(false);
mDismissCallbackRegistry.notifyDismissSucceeded();
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
@@ -2567,66 +2500,6 @@
Trace.endSection();
}
- private void handleNotifyScreenTurningOn(IKeyguardDrawnCallback callback) {
- Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurningOn");
- synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleNotifyScreenTurningOn");
-
- mPendingDrawnTasks.reset();
-
- if (mUnfoldLightRevealAnimation.isPresent()) {
- mUnfoldLightRevealAnimation.get()
- .onScreenTurningOn(mPendingDrawnTasks.registerTask("unfold-reveal"));
- }
-
- if (mFoldAodAnimationController.isPresent()) {
- mFoldAodAnimationController.get()
- .onScreenTurningOn(mPendingDrawnTasks.registerTask("fold-to-aod"));
- }
-
- mKeyguardViewControllerLazy.get().onScreenTurningOn();
- if (callback != null) {
- if (mWakeAndUnlocking) {
- mWakeAndUnlockingDrawnCallback =
- mPendingDrawnTasks.registerTask("wake-and-unlocking");
- }
- }
-
- mPendingDrawnTasks.onTasksComplete(() -> notifyDrawn(callback));
- }
- Trace.endSection();
- }
-
- private void handleNotifyScreenTurnedOn() {
- Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurnedOn");
- synchronized (this) {
- if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOn");
-
- mPendingDrawnTasks.reset();
- mKeyguardViewControllerLazy.get().onScreenTurnedOn();
- }
- Trace.endSection();
- }
-
- private void handleNotifyScreenTurnedOff() {
- synchronized (this) {
- if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOff");
- mWakeAndUnlockingDrawnCallback = null;
- }
- }
-
- private void notifyDrawn(final IKeyguardDrawnCallback callback) {
- Trace.beginSection("KeyguardViewMediator#notifyDrawn");
- try {
- if (callback != null) {
- callback.onDrawn();
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception calling onDrawn():", e);
- }
- Trace.endSection();
- }
-
private void resetKeyguardDonePendingLocked() {
mKeyguardDonePending = false;
mHandler.removeMessages(KEYGUARD_DONE_PENDING_TIMEOUT);
@@ -2650,7 +2523,7 @@
public void onWakeAndUnlocking() {
Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
- mWakeAndUnlocking = true;
+ mScreenOnCoordinator.setWakeAndUnlocking(true);
keyguardDone();
Trace.endSection();
}
@@ -2762,10 +2635,6 @@
return mViewMediatorCallback;
}
- public LockPatternUtils getLockPatternUtils() {
- return mLockPatternUtils;
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print(" mSystemReady: "); pw.println(mSystemReady);
@@ -2789,9 +2658,7 @@
pw.print(" mHideAnimationRun: "); pw.println(mHideAnimationRun);
pw.print(" mPendingReset: "); pw.println(mPendingReset);
pw.print(" mPendingLock: "); pw.println(mPendingLock);
- pw.print(" mPendingDrawnTasks: "); pw.println(mPendingDrawnTasks.getPendingCount());
- pw.print(" mWakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
- pw.print(" mDrawnCallback: "); pw.println(mWakeAndUnlockingDrawnCallback);
+ pw.print(" wakeAndUnlocking: "); pw.println(mScreenOnCoordinator.getWakeAndUnlocking());
}
/**
@@ -2827,13 +2694,6 @@
}
/**
- * @param pulsing true when device temporarily wakes up to display an incoming notification.
- */
- public void setPulsing(boolean pulsing) {
- mPulsing = pulsing;
- }
-
- /**
* Set if the wallpaper supports ambient mode. This is used to trigger the right animation.
* In case it does support it, we have to fade in the incoming app, otherwise we'll reveal it
* with the light reveal scrim.
@@ -2872,7 +2732,7 @@
}
private void setShowingLocked(boolean showing, boolean forceCallbacks) {
- final boolean aodShowing = mDozing && !mWakeAndUnlocking;
+ final boolean aodShowing = mDozing && !mScreenOnCoordinator.getWakeAndUnlocking();
final boolean notifyDefaultDisplayCallbacks = showing != mShowing
|| aodShowing != mAodShowing || forceCallbacks;
mShowing = showing;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/Lifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/Lifecycle.java
index 3da6caf..b870f58 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/Lifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/Lifecycle.java
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Objects;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -42,4 +43,13 @@
consumer.accept(mObservers.get(i));
}
}
+
+ /**
+ * Will dispatch the consumer to the observer, along with a single argument of type<U>.
+ */
+ public <U> void dispatch(BiConsumer<T, U> biConsumer, U arg) {
+ for (int i = 0; i < mObservers.size(); i++) {
+ biConsumer.accept(mObservers.get(i), arg);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
index d17c39a..e71aa85 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
@@ -18,6 +18,8 @@
import android.os.Trace;
+import androidx.annotation.NonNull;
+
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
@@ -49,9 +51,14 @@
return mScreenState;
}
- public void dispatchScreenTurningOn() {
+ /**
+ * Dispatch screen turning on events to the registered observers
+ *
+ * @param onDrawn Invoke to notify the caller that the event has been processed
+ */
+ public void dispatchScreenTurningOn(@NonNull Runnable onDrawn) {
setScreenState(SCREEN_TURNING_ON);
- dispatch(Observer::onScreenTurningOn);
+ dispatch(Observer::onScreenTurningOn, onDrawn);
}
public void dispatchScreenTurnedOn() {
@@ -81,7 +88,12 @@
}
public interface Observer {
- default void onScreenTurningOn() {}
+ /**
+ * Receive the screen turning on event
+ *
+ * @param onDrawn Invoke to notify the caller that the event has been processed
+ */
+ default void onScreenTurningOn(@NonNull Runnable onDrawn) {}
default void onScreenTurnedOn() {}
default void onScreenTurningOff() {}
default void onScreenTurnedOff() {}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index eecb55b..dd844e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -31,6 +31,7 @@
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
+import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
@@ -50,11 +51,9 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.AsyncSensorManager;
-import java.util.Optional;
import java.util.concurrent.Executor;
import dagger.Lazy;
@@ -93,12 +92,12 @@
NavigationModeController navigationModeController,
KeyguardDisplayManager keyguardDisplayManager,
DozeParameters dozeParameters,
- Optional<SysUIUnfoldComponent> unfoldComponent,
SysuiStatusBarStateController statusBarStateController,
KeyguardStateController keyguardStateController,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
ScreenOffAnimationController screenOffAnimationController,
Lazy<NotificationShadeDepthController> notificationShadeDepthController,
+ ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor) {
return new KeyguardViewMediator(
context,
@@ -117,12 +116,12 @@
navigationModeController,
keyguardDisplayManager,
dozeParameters,
- unfoldComponent,
statusBarStateController,
keyguardStateController,
keyguardUnlockAnimationController,
screenOffAnimationController,
notificationShadeDepthController,
+ screenOnCoordinator,
interactionJankMonitor
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index 492fdc6..b15807c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -66,11 +66,12 @@
* @param poolSize The maximum amount that the size of the buffer is allowed to flex in response to
* sequential calls to [document] that aren't immediately followed by a matching call to [push].
*/
-class LogBuffer(
+class LogBuffer @JvmOverloads constructor(
private val name: String,
private val maxLogs: Int,
private val poolSize: Int,
- private val logcatEchoTracker: LogcatEchoTracker
+ private val logcatEchoTracker: LogcatEchoTracker,
+ private val systrace: Boolean = true
) {
init {
if (maxLogs < poolSize) {
@@ -175,6 +176,10 @@
buffer.removeFirst()
}
buffer.add(message as LogMessageImpl)
+ if (systrace) {
+ val messageStr = message.printer(message)
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", "$name - $messageStr")
+ }
if (logcatEchoTracker.isBufferLoggable(name, message.level) ||
logcatEchoTracker.isTagLoggable(message.tag, message.level)) {
echo(message)
@@ -237,7 +242,6 @@
LogLevel.ERROR -> Log.e(message.tag, strMessage)
LogLevel.WTF -> Log.wtf(message.tag, strMessage)
}
- Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", strMessage)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
index 5296ee6..cbfca25 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
@@ -36,8 +36,13 @@
}
@JvmOverloads
- fun create(name: String, maxPoolSize: Int, flexSize: Int = 10): LogBuffer {
- val buffer = LogBuffer(name, poolLimit(maxPoolSize), flexSize, logcatEchoTracker)
+ fun create(
+ name: String,
+ maxPoolSize: Int,
+ flexSize: Int = 10,
+ systrace: Boolean = true
+ ): LogBuffer {
+ val buffer = LogBuffer(name, poolLimit(maxPoolSize), flexSize, logcatEchoTracker, systrace)
dumpManager.registerBuffer(name, buffer)
return buffer
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index d3bd0a5..1f953d7 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -49,7 +49,8 @@
@SysUISingleton
@NotificationLog
public static LogBuffer provideNotificationsLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifLog", 1000);
+ return factory.create("NotifLog", 1000 /* maxPoolSize */,
+ 10 /* flexSize */, false /* systrace */);
}
/** Provides a logging buffer for all logs related to the data layer of notifications. */
@@ -65,7 +66,8 @@
@SysUISingleton
@NotificationSectionLog
public static LogBuffer provideNotificationSectionLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifSectionLog", 1000);
+ return factory.create("NotifSectionLog", 1000 /* maxPoolSize */,
+ 10 /* flexSize */, false /* systrace */);
}
/** Provides a logging buffer for all logs related to the data layer of notifications. */
@@ -81,7 +83,8 @@
@SysUISingleton
@QSLog
public static LogBuffer provideQuickSettingsLogBuffer(LogBufferFactory factory) {
- return factory.create("QSLog", 500);
+ return factory.create("QSLog", 500 /* maxPoolSize */,
+ 10 /* flexSize */, false /* systrace */);
}
/** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */
@@ -89,7 +92,8 @@
@SysUISingleton
@BroadcastDispatcherLog
public static LogBuffer provideBroadcastDispatcherLogBuffer(LogBufferFactory factory) {
- return factory.create("BroadcastDispatcherLog", 500);
+ return factory.create("BroadcastDispatcherLog", 500 /* maxPoolSize */,
+ 10 /* flexSize */, false /* systrace */);
}
/** Provides a logging buffer for all logs related to Toasts shown by SystemUI. */
@@ -127,7 +131,8 @@
@SysUISingleton
@QSFragmentDisableLog
public static LogBuffer provideQSFragmentDisableLogBuffer(LogBufferFactory factory) {
- return factory.create("QSFragmentDisableFlagsLog", 10);
+ return factory.create("QSFragmentDisableFlagsLog", 10 /* maxPoolSize */,
+ 10 /* flexSize */, false /* systrace */);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 99b283e..5a86723 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -71,20 +71,32 @@
when (args[1]) {
MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> {
mediaTttChipControllerSender.displayChip(
- MoveCloserToTransfer(appIconDrawable, otherDeviceName)
+ MoveCloserToTransfer(
+ appIconDrawable, APP_ICON_CONTENT_DESCRIPTION, otherDeviceName
+ )
)
}
TRANSFER_INITIATED_COMMAND_NAME -> {
val futureTask = FutureTask { fakeUndoRunnable }
mediaTttChipControllerSender.displayChip(
- TransferInitiated(appIconDrawable, otherDeviceName, futureTask)
+ TransferInitiated(
+ appIconDrawable,
+ APP_ICON_CONTENT_DESCRIPTION,
+ otherDeviceName,
+ futureTask
+ )
)
mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME)
}
TRANSFER_SUCCEEDED_COMMAND_NAME -> {
mediaTttChipControllerSender.displayChip(
- TransferSucceeded(appIconDrawable, otherDeviceName, fakeUndoRunnable)
+ TransferSucceeded(
+ appIconDrawable,
+ APP_ICON_CONTENT_DESCRIPTION,
+ otherDeviceName,
+ fakeUndoRunnable
+ )
)
}
else -> {
@@ -118,7 +130,9 @@
/** A command to DISPLAY the media ttt chip on the RECEIVER device. */
inner class AddChipCommandReceiver : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
- mediaTttChipControllerReceiver.displayChip(ChipStateReceiver(appIconDrawable))
+ mediaTttChipControllerReceiver.displayChip(
+ ChipStateReceiver(appIconDrawable, APP_ICON_CONTENT_DESCRIPTION)
+ )
}
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_RECEIVER_TAG")
@@ -156,4 +170,5 @@
val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
private const val FUTURE_WAIT_TIME = 2000L
+private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"
private const val TAG = "MediaTapToTransferCli"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 3b429c8..67721a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -99,6 +99,7 @@
internal fun setIcon(chipState: T, currentChipView: ViewGroup) {
currentChipView.findViewById<CachingIconView>(R.id.app_icon).apply {
this.setImageDrawable(chipState.appIconDrawable)
+ this.contentDescription = chipState.appIconContentDescription
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
index 1e475a5..c510cbb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
@@ -22,7 +22,9 @@
* A superclass chip state that will be subclassed by the sender chip and receiver chip.
*
* @property appIconDrawable a drawable representing the icon of the app playing the media.
+ * @property appIconContentDescription a string to use as the content description for the icon.
*/
open class MediaTttChipState(
- internal val appIconDrawable: Drawable
+ internal val appIconDrawable: Drawable,
+ internal val appIconContentDescription: String
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
index 5397235..df6b934 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
@@ -24,5 +24,6 @@
* the receiver device.
*/
class ChipStateReceiver(
- appIconDrawable: Drawable
-) : MediaTttChipState(appIconDrawable)
+ appIconDrawable: Drawable,
+ appIconContentDescription: String
+) : MediaTttChipState(appIconDrawable, appIconContentDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index 24943b9..b1f6faa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -34,9 +34,10 @@
*/
sealed class ChipStateSender(
appIconDrawable: Drawable,
+ appIconContentDescription: String,
@StringRes internal val chipText: Int,
internal val otherDeviceName: String,
-) : MediaTttChipState(appIconDrawable)
+) : MediaTttChipState(appIconDrawable, appIconContentDescription)
/**
* A state representing that the two devices are close but not close enough to initiate a transfer.
@@ -44,8 +45,14 @@
*/
class MoveCloserToTransfer(
appIconDrawable: Drawable,
+ appIconContentDescription: String,
otherDeviceName: String,
-) : ChipStateSender(appIconDrawable, R.string.media_move_closer_to_transfer, otherDeviceName)
+) : ChipStateSender(
+ appIconDrawable,
+ appIconContentDescription,
+ R.string.media_move_closer_to_transfer,
+ otherDeviceName
+)
/**
* A state representing that a transfer has been initiated (but not completed).
@@ -57,9 +64,15 @@
*/
class TransferInitiated(
appIconDrawable: Drawable,
+ appIconContentDescription: String,
otherDeviceName: String,
val future: Future<Runnable?>
-) : ChipStateSender(appIconDrawable, R.string.media_transfer_playing, otherDeviceName)
+) : ChipStateSender(
+ appIconDrawable,
+ appIconContentDescription,
+ R.string.media_transfer_playing,
+ otherDeviceName
+)
/**
* A state representing that a transfer has been successfully completed.
@@ -69,6 +82,11 @@
*/
class TransferSucceeded(
appIconDrawable: Drawable,
+ appIconContentDescription: String,
otherDeviceName: String,
val undoRunnable: Runnable? = null
-) : ChipStateSender(appIconDrawable, R.string.media_transfer_playing, otherDeviceName)
+) : ChipStateSender(appIconDrawable,
+ appIconContentDescription,
+ R.string.media_transfer_playing,
+ otherDeviceName
+)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index fce4b98..77d3d70 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -93,7 +93,10 @@
mainExecutor.execute {
displayChip(
TransferSucceeded(
- chipState.appIconDrawable, chipState.otherDeviceName, undoRunnable
+ chipState.appIconDrawable,
+ chipState.appIconContentDescription,
+ chipState.otherDeviceName,
+ undoRunnable
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 3e2da72..6d1f8f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -30,6 +30,7 @@
import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
public class PagedTileLayout extends ViewPager implements QSTileLayout {
@@ -332,6 +333,18 @@
mPageListener = listener;
}
+ public List<String> getSpecsForPage(int page) {
+ ArrayList<String> out = new ArrayList<>();
+ if (page < 0) return out;
+ int perPage = mPages.get(0).maxTiles();
+ int startOfPage = page * perPage;
+ int endOfPage = (page + 1) * perPage;
+ for (int i = startOfPage; i < endOfPage && i < mTiles.size(); i++) {
+ out.add(mTiles.get(i).tile.getTileSpec());
+ }
+ return out;
+ }
+
private void distributeTiles() {
emptyAndInflateOrRemovePages();
@@ -491,6 +504,11 @@
return currentPage.mRecords.size();
}
+ public int getNumTilesFirstPage() {
+ if (mPages.size() == 0) return 0;
+ return mPages.get(0).mRecords.size();
+ }
+
public void startTileReveal(Set<String> tileSpecs, final Runnable postAnimation) {
if (tileSpecs.isEmpty() || mPages.size() < 2 || getScrollX() != 0 || !beginFakeDrag()) {
// Do not start the reveal animation unless there are tiles to animate, multiple
@@ -556,8 +574,9 @@
updateSelected();
if (mPageIndicator == null) return;
if (mPageListener != null) {
+ int pageNumber = isLayoutRtl() ? mPages.size() - 1 - position : position;
mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
- : position == 0);
+ : position == 0, pageNumber);
}
}
@@ -575,8 +594,12 @@
mPageIndicatorPosition = position + positionOffset;
mPageIndicator.setLocation(mPageIndicatorPosition);
if (mPageListener != null) {
+ int pageNumber = isLayoutRtl() ? mPages.size() - 1 - position : position;
mPageListener.onPageChanged(positionOffsetPixels == 0 &&
- (isLayoutRtl() ? position == mPages.size() - 1 : position == 0));
+ (isLayoutRtl() ? position == mPages.size() - 1 : position == 0),
+ // Send only valid page number on integer pages
+ positionOffsetPixels == 0 ? pageNumber : PageListener.INVALID_PAGE
+ );
}
}
@@ -626,6 +649,7 @@
};
public interface PageListener {
- void onPageChanged(boolean isFirst);
+ int INVALID_PAGE = -1;
+ void onPageChanged(boolean isFirst, int pageNumber);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 44d5e21..6c7d6e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -20,6 +20,8 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnLayoutChangeListener;
@@ -57,11 +59,14 @@
private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim";
private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
- public static final float EXPANDED_TILE_DELAY = .86f;
+ private static final float EXPANDED_TILE_DELAY = .86f;
+ //Non first page delays
+ private static final float QS_TILE_LABEL_FADE_OUT_START = 0.15f;
+ private static final float QS_TILE_LABEL_FADE_OUT_END = 0.7f;
+ private static final float QQS_FADE_IN_INTERVAL = 0.1f;
+
public static final float SHORT_PARALLAX_AMOUNT = 0.1f;
- private static final long QQS_FADE_IN_DURATION = 200L;
- // Fade out faster than fade in to finish before QQS hides.
- private static final long QQS_FADE_OUT_DURATION = 50L;
+
/**
* List of all views that will be reset when clearing animation state
@@ -86,33 +91,54 @@
private PagedTileLayout mPagedLayout;
private boolean mOnFirstPage = true;
- private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
+ private int mCurrentPage = 0;
+ private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
+ // Animator for elements in the first page, including secondary labels and qqs brightness
+ // slider, as well as animating the alpha of the QS tile layout (as we are tracking QQS tiles)
private TouchAnimator mFirstPageAnimator;
- private TouchAnimator mFirstPageDelayedAnimator;
+ // TranslationX animator for QQS/QS tiles
private TouchAnimator mTranslationXAnimator;
+ // TranslationY animator for QS tiles (and their components) in the first page
private TouchAnimator mTranslationYAnimator;
- private TouchAnimator mNonfirstPageAnimator;
- private TouchAnimator mNonfirstPageDelayedAnimator;
+ // TranslationY animator for QQS tiles (and their components)
+ private TouchAnimator mQQSTranslationYAnimator;
+ // Animates alpha of permanent views (QS tile layout, QQS tiles) when not in first page
+ private TouchAnimator mNonfirstPageAlphaAnimator;
+ // TranslatesY the QS Tile layout using QS.getHeightDiff()
+ private TouchAnimator mQSTileLayoutTranslatorAnimator;
// This animates fading of SecurityFooter and media divider
private TouchAnimator mAllPagesDelayedAnimator;
+ // Animator for brightness slider(s)
private TouchAnimator mBrightnessAnimator;
+ // Animator for Footer actions in QQS
private TouchAnimator mQQSFooterActionsAnimator;
+ // Height animator for QQS tiles (height changing from QQS size to QS size)
private HeightExpansionAnimator mQQSTileHeightAnimator;
- private HeightExpansionAnimator mOtherTilesExpandAnimator;
+ // Height animator for QS tile in first page but not in QQS, to present the illusion that they
+ // are expanding alongside the QQS tiles
+ private HeightExpansionAnimator mOtherFirstPageTilesHeightAnimator;
+ // Pair of animators for each non first page. The creation is delayed until the user first
+ // scrolls to that page, in order to get the proper measures and layout.
+ private final SparseArray<Pair<HeightExpansionAnimator, TouchAnimator>>
+ mNonFirstPageQSAnimators = new SparseArray<>();
private boolean mNeedsAnimatorUpdate = false;
- private boolean mToShowing;
private boolean mOnKeyguard;
private boolean mAllowFancy;
private boolean mFullRows;
private int mNumQuickTiles;
+ private int mLastQQSTileHeight;
private float mLastPosition;
private final QSTileHost mHost;
private final Executor mExecutor;
private final TunerService mTunerService;
private boolean mShowCollapsedOnKeyguard;
private boolean mTranslateWhileExpanding;
+ private int mQQSTop;
+
+ private int[] mTmpLoc1 = new int[2];
+ private int[] mTmpLoc2 = new int[2];
@Inject
public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader,
@@ -213,8 +239,19 @@
updateAnimators();
}
+ private void addNonFirstPageAnimators(int page) {
+ Pair<HeightExpansionAnimator, TouchAnimator> pair = createSecondaryPageAnimators(page);
+ mNonFirstPageQSAnimators.put(page, pair);
+ }
+
@Override
- public void onPageChanged(boolean isFirst) {
+ public void onPageChanged(boolean isFirst, int currentPage) {
+ if (currentPage != INVALID_PAGE && mCurrentPage != currentPage) {
+ mCurrentPage = currentPage;
+ if (!isFirst && !mNonFirstPageQSAnimators.contains(currentPage)) {
+ addNonFirstPageAnimators(currentPage);
+ }
+ }
if (mOnFirstPage == isFirst) return;
if (!isFirst) {
clearAnimationState();
@@ -230,7 +267,8 @@
int yOffset,
int[] temp,
TouchAnimator.Builder animatorBuilderX,
- TouchAnimator.Builder animatorBuilderY
+ TouchAnimator.Builder animatorBuilderY,
+ TouchAnimator.Builder qqsAnimatorBuilderY
) {
getRelativePosition(temp, qqsView, commonParent);
int qqsPosX = temp[0];
@@ -243,7 +281,7 @@
animatorBuilderX.addFloat(qqsView, "translationX", 0, xDiff);
animatorBuilderX.addFloat(qsView, "translationX", -xDiff, 0);
int yDiff = qsPosY - qqsPosY - yOffset;
- animatorBuilderY.addFloat(qqsView, "translationY", 0, yDiff);
+ qqsAnimatorBuilderY.addFloat(qqsView, "translationY", 0, yDiff);
animatorBuilderY.addFloat(qsView, "translationY", -yDiff, 0);
mAllViews.add(qqsView);
mAllViews.add(qsView);
@@ -253,40 +291,47 @@
mNeedsAnimatorUpdate = false;
TouchAnimator.Builder firstPageBuilder = new Builder();
TouchAnimator.Builder translationYBuilder = new Builder();
+ TouchAnimator.Builder qqsTranslationYBuilder = new Builder();
TouchAnimator.Builder translationXBuilder = new Builder();
+ TouchAnimator.Builder nonFirstPageAlphaBuilder = new Builder();
Collection<QSTile> tiles = mHost.getTiles();
int count = 0;
- int[] loc1 = new int[2];
- int[] loc2 = new int[2];
clearAnimationState();
+ mNonFirstPageQSAnimators.clear();
mAllViews.clear();
mAnimatedQsViews.clear();
mQQSTileHeightAnimator = null;
- mOtherTilesExpandAnimator = null;
+ mOtherFirstPageTilesHeightAnimator = null;
mNumQuickTiles = mQuickQsPanel.getNumQuickTiles();
QSTileLayout tileLayout = mQsPanelController.getTileLayout();
mAllViews.add((View) tileLayout);
- int height = mQs.getView() != null ? mQs.getView().getMeasuredHeight() : 0;
- int heightDiff = height - mQs.getHeader().getBottom()
- + mQs.getHeader().getPaddingBottom();
+ int heightDiff = mQs.getHeightDiff();
if (!mTranslateWhileExpanding) {
heightDiff *= SHORT_PARALLAX_AMOUNT;
}
- firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0);
+ mQSTileLayoutTranslatorAnimator = new Builder()
+ .addFloat(tileLayout, "translationY", heightDiff, 0)
+ .build();
- int qqsTileHeight = 0;
+ mLastQQSTileHeight = 0;
if (mQsPanelController.areThereTiles()) {
for (QSTile tile : tiles) {
QSTileView tileView = mQsPanelController.getTileView(tile);
+
if (tileView == null) {
Log.e(TAG, "tileView is null " + tile.getTileSpec());
continue;
}
+ // Only animate tiles in the first page
+ if (mPagedLayout != null && count >= mPagedLayout.getNumTilesFirstPage()) {
+ break;
+ }
+
final View tileIcon = tileView.getIcon().getIconView();
View view = mQs.getView();
@@ -297,16 +342,16 @@
QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
if (quickTileView == null) continue;
- getRelativePosition(loc1, quickTileView, view);
- getRelativePosition(loc2, tileView, view);
- int yOffset = loc2[1] - loc1[1];
- int xOffset = loc2[0] - loc1[0];
+ getRelativePosition(mTmpLoc1, quickTileView, view);
+ getRelativePosition(mTmpLoc2, tileView, view);
+ int yOffset = mTmpLoc2[1] - mTmpLoc1[1];
+ int xOffset = mTmpLoc2[0] - mTmpLoc1[0];
// Offset the translation animation on the views
// (that goes from 0 to getOffsetTranslation)
int offsetWithQSBHTranslation =
yOffset - mQuickStatusBarHeader.getOffsetTranslation();
- translationYBuilder.addFloat(quickTileView, "translationY", 0,
+ qqsTranslationYBuilder.addFloat(quickTileView, "translationY", 0,
offsetWithQSBHTranslation);
translationYBuilder.addFloat(tileView, "translationY",
-offsetWithQSBHTranslation, 0);
@@ -317,7 +362,7 @@
if (mQQSTileHeightAnimator == null) {
mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
quickTileView.getMeasuredHeight(), tileView.getMeasuredHeight());
- qqsTileHeight = quickTileView.getMeasuredHeight();
+ mLastQQSTileHeight = quickTileView.getMeasuredHeight();
}
mQQSTileHeightAnimator.addView(quickTileView);
@@ -329,9 +374,10 @@
view,
xOffset,
yOffset,
- loc1,
+ mTmpLoc1,
translationXBuilder,
- translationYBuilder
+ translationYBuilder,
+ qqsTranslationYBuilder
);
// Label containers
@@ -341,9 +387,10 @@
view,
xOffset,
yOffset,
- loc1,
+ mTmpLoc1,
translationXBuilder,
- translationYBuilder
+ translationYBuilder,
+ qqsTranslationYBuilder
);
// Secondary icon
@@ -353,12 +400,15 @@
view,
xOffset,
yOffset,
- loc1,
+ mTmpLoc1,
translationXBuilder,
- translationYBuilder
+ translationYBuilder,
+ qqsTranslationYBuilder
);
firstPageBuilder.addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1);
+ nonFirstPageAlphaBuilder
+ .addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 0);
mAnimatedQsViews.add(tileView);
mAllViews.add(quickTileView);
@@ -373,16 +423,17 @@
// expanding from.
SideLabelTileLayout qqsLayout =
(SideLabelTileLayout) mQuickQsPanel.getTileLayout();
- getRelativePosition(loc1, qqsLayout, view);
- getRelativePosition(loc2, tileView, view);
- int diff = loc2[1] - (loc1[1] + qqsLayout.getPhantomTopPosition(count));
+ getRelativePosition(mTmpLoc1, qqsLayout, view);
+ mQQSTop = mTmpLoc1[1];
+ getRelativePosition(mTmpLoc2, tileView, view);
+ int diff = mTmpLoc2[1] - (mTmpLoc1[1] + qqsLayout.getPhantomTopPosition(count));
translationYBuilder.addFloat(tileView, "translationY", -diff, 0);
- if (mOtherTilesExpandAnimator == null) {
- mOtherTilesExpandAnimator =
+ if (mOtherFirstPageTilesHeightAnimator == null) {
+ mOtherFirstPageTilesHeightAnimator =
new HeightExpansionAnimator(
- this, qqsTileHeight, tileView.getMeasuredHeight());
+ this, mLastQQSTileHeight, tileView.getMeasuredHeight());
}
- mOtherTilesExpandAnimator.addView(tileView);
+ mOtherFirstPageTilesHeightAnimator.addView(tileView);
tileView.setClipChildren(true);
tileView.setClipToPadding(true);
firstPageBuilder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 1);
@@ -392,18 +443,19 @@
mAllViews.add(tileView);
count++;
}
+ if (mCurrentPage != 0) {
+ addNonFirstPageAnimators(mCurrentPage);
+ }
}
if (mAllowFancy) {
animateBrightnessSlider(firstPageBuilder);
mFirstPageAnimator = firstPageBuilder
+ // Fade in the tiles/labels as we reach the final position.
+ .addFloat(tileLayout, "alpha", 0, 1)
.setListener(this)
.build();
- // Fade in the tiles/labels as we reach the final position.
- Builder builder = new Builder()
- .addFloat(tileLayout, "alpha", 0, 1);
- mFirstPageDelayedAnimator = builder.build();
if (mQQSFooterActions.getVisibility() != View.GONE) {
// only when qqs footer is present (which means split shade mode) it needs to
@@ -411,9 +463,8 @@
updateQQSFooterAnimation();
}
-
// Fade in the security footer and the divider as we reach the final position
- builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
+ Builder builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
builder.addFloat(mSecurityFooter.getView(), "alpha", 0, 1);
if (mQsPanelController.shouldUseHorizontalLayout()
&& mQsPanelController.mMediaHost.hostView != null) {
@@ -425,26 +476,100 @@
mAllPagesDelayedAnimator = builder.build();
mAllViews.add(mSecurityFooter.getView());
translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
+ qqsTranslationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
+ if (mOnFirstPage) {
+ // Only recreate this animator if we're in the first page. That way we know that
+ // the first page is attached and has the proper positions/measures.
+ mQQSTranslationYAnimator = qqsTranslationYBuilder.build();
+ }
mTranslationYAnimator = translationYBuilder.build();
mTranslationXAnimator = translationXBuilder.build();
if (mQQSTileHeightAnimator != null) {
mQQSTileHeightAnimator.setInterpolator(
mQSExpansionPathInterpolator.getYInterpolator());
}
- if (mOtherTilesExpandAnimator != null) {
- mOtherTilesExpandAnimator.setInterpolator(
+ if (mOtherFirstPageTilesHeightAnimator != null) {
+ mOtherFirstPageTilesHeightAnimator.setInterpolator(
mQSExpansionPathInterpolator.getYInterpolator());
}
}
- mNonfirstPageAnimator = new TouchAnimator.Builder()
+ mNonfirstPageAlphaAnimator = nonFirstPageAlphaBuilder
.addFloat(mQuickQsPanel, "alpha", 1, 0)
+ .addFloat(tileLayout, "alpha", 0, 1)
.setListener(mNonFirstPageListener)
- .setEndDelay(.5f)
+ .setEndDelay(1 - QQS_FADE_IN_INTERVAL)
.build();
- mNonfirstPageDelayedAnimator = new TouchAnimator.Builder()
- .setStartDelay(.14f)
- .addFloat(tileLayout, "alpha", 0, 1).build();
+ }
+
+ private Pair<HeightExpansionAnimator, TouchAnimator> createSecondaryPageAnimators(int page) {
+ if (mPagedLayout == null) return null;
+ HeightExpansionAnimator animator = null;
+ TouchAnimator.Builder builder = new Builder()
+ .setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
+ TouchAnimator.Builder alphaDelayedBuilder = new Builder()
+ .setStartDelay(QS_TILE_LABEL_FADE_OUT_START)
+ .setEndDelay(QS_TILE_LABEL_FADE_OUT_END);
+ SideLabelTileLayout qqsLayout = (SideLabelTileLayout) mQuickQsPanel.getTileLayout();
+ View view = mQs.getView();
+ List<String> specs = mPagedLayout.getSpecsForPage(page);
+
+ int row = -1;
+ int lastTileTop = -1;
+
+ for (int i = 0; i < specs.size(); i++) {
+ QSTileView tileView = mQsPanelController.getTileView(specs.get(i));
+ getRelativePosition(mTmpLoc2, tileView, view);
+ int diff = mTmpLoc2[1] - (mQQSTop + qqsLayout.getPhantomTopPosition(i));
+ builder.addFloat(tileView, "translationY", -diff, 0);
+ // The different elements in the tile should be centered, so maintain them centered
+ int centerDiff = (tileView.getMeasuredHeight() - mLastQQSTileHeight) / 2;
+ builder.addFloat(tileView.getIcon(), "translationY", -centerDiff, 0);
+ builder.addFloat(tileView.getSecondaryIcon(), "translationY", -centerDiff, 0);
+ // The labels have different apparent size in QQS vs QS (no secondary label), so the
+ // translation needs to account for that.
+ int labelDiff = centerDiff - tileView.getSecondaryLabel().getMeasuredHeight() / 2;
+ builder.addFloat(tileView.getLabelContainer(), "translationY", -labelDiff, 0);
+ builder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 0.3f, 1);
+
+ alphaDelayedBuilder.addFloat(tileView.getLabelContainer(), "alpha", 0, 1);
+ alphaDelayedBuilder.addFloat(tileView.getIcon(), "alpha", 0, 1);
+ alphaDelayedBuilder.addFloat(tileView.getSecondaryIcon(), "alpha", 0, 1);
+
+ final int tileTop = tileView.getTop();
+ if (tileTop != lastTileTop) {
+ row++;
+ lastTileTop = tileTop;
+ }
+ if (i >= mQuickQsPanel.getTileLayout().getNumVisibleTiles() && row >= 2) {
+ // Fade completely the tiles in rows below the ones that will merge into QQS.
+ // args is an array of 0s where the length is the current row index (at least third
+ // row)
+ final float[] args = new float[row];
+ args[args.length - 1] = 1f;
+ builder.addFloat(tileView, "alpha", args);
+ } else {
+ // For all the other rows, fade them a bit
+ builder.addFloat(tileView, "alpha", 0.6f, 1);
+ }
+
+ if (animator == null) {
+ animator = new HeightExpansionAnimator(
+ this, mLastQQSTileHeight, tileView.getMeasuredHeight());
+ animator.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
+ }
+ animator.addView(tileView);
+
+ tileView.setClipChildren(true);
+ tileView.setClipToPadding(true);
+ mAllViews.add(tileView);
+ mAllViews.add(tileView.getSecondaryLabel());
+ mAllViews.add(tileView.getIcon());
+ mAllViews.add(tileView.getSecondaryIcon());
+ mAllViews.add(tileView.getLabelContainer());
+ }
+ builder.addFloat(alphaDelayedBuilder.build(), "position", 0, 1);
+ return new Pair<>(animator, builder.build());
}
private void animateBrightnessSlider(Builder firstPageBuilder) {
@@ -542,30 +667,36 @@
}
}
mLastPosition = position;
- if (mOnFirstPage && mAllowFancy) {
+ if (!mAllowFancy) return;
+ if (mOnFirstPage) {
mQuickQsPanel.setAlpha(1);
mFirstPageAnimator.setPosition(position);
- mFirstPageDelayedAnimator.setPosition(position);
mTranslationYAnimator.setPosition(position);
mTranslationXAnimator.setPosition(position);
- if (mQQSTileHeightAnimator != null) {
- mQQSTileHeightAnimator.setPosition(position);
- }
- if (mOtherTilesExpandAnimator != null) {
- mOtherTilesExpandAnimator.setPosition(position);
+ if (mOtherFirstPageTilesHeightAnimator != null) {
+ mOtherFirstPageTilesHeightAnimator.setPosition(position);
}
} else {
- mNonfirstPageAnimator.setPosition(position);
- mNonfirstPageDelayedAnimator.setPosition(position);
+ mNonfirstPageAlphaAnimator.setPosition(position);
}
- if (mAllowFancy) {
- mAllPagesDelayedAnimator.setPosition(position);
- if (mBrightnessAnimator != null) {
- mBrightnessAnimator.setPosition(position);
+ for (int i = 0; i < mNonFirstPageQSAnimators.size(); i++) {
+ Pair<HeightExpansionAnimator, TouchAnimator> pair = mNonFirstPageQSAnimators.valueAt(i);
+ if (pair != null) {
+ pair.first.setPosition(position);
+ pair.second.setPosition(position);
}
- if (mQQSFooterActionsAnimator != null) {
- mQQSFooterActionsAnimator.setPosition(position);
- }
+ }
+ if (mQQSTileHeightAnimator != null) {
+ mQQSTileHeightAnimator.setPosition(position);
+ }
+ mQSTileLayoutTranslatorAnimator.setPosition(position);
+ mQQSTranslationYAnimator.setPosition(position);
+ mAllPagesDelayedAnimator.setPosition(position);
+ if (mBrightnessAnimator != null) {
+ mBrightnessAnimator.setPosition(position);
+ }
+ if (mQQSFooterActionsAnimator != null) {
+ mQQSFooterActionsAnimator.setPosition(position);
}
}
@@ -611,8 +742,11 @@
if (mQQSTileHeightAnimator != null) {
mQQSTileHeightAnimator.resetViewsHeights();
}
- if (mOtherTilesExpandAnimator != null) {
- mOtherTilesExpandAnimator.resetViewsHeights();
+ if (mOtherFirstPageTilesHeightAnimator != null) {
+ mOtherFirstPageTilesHeightAnimator.resetViewsHeights();
+ }
+ for (int i = 0; i < mNonFirstPageQSAnimators.size(); i++) {
+ mNonFirstPageQSAnimators.valueAt(i).first.resetViewsHeights();
}
final int N2 = mAnimatedQsViews.size();
for (int i = 0; i < N2; i++) {
@@ -623,7 +757,9 @@
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
- mExecutor.execute(mUpdateAnimators);
+ boolean actualChange =
+ left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom;
+ if (actualChange) mExecutor.execute(mUpdateAnimators);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index e82e9d2..bb8a1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -504,6 +504,12 @@
}
@Override
+ public int getHeightDiff() {
+ return mQSPanelScrollView.getBottom() - mHeader.getBottom()
+ + mHeader.getPaddingBottom();
+ }
+
+ @Override
public void setQsExpansion(float expansion, float panelExpansionFraction,
float proposedTranslation, float squishinessFraction) {
float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
@@ -537,8 +543,7 @@
boolean fullyExpanded = expansion == 1;
boolean fullyCollapsed = expansion == 0.0f;
- int heightDiff = mQSPanelScrollView.getBottom() - mHeader.getBottom()
- + mHeader.getPaddingBottom();
+ int heightDiff = getHeightDiff();
float panelTranslationY = translationScaleY * heightDiff;
// Let the views animate their contents correctly by giving them the necessary context.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 20c0fdd..38061a8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -106,6 +107,7 @@
protected QSTileLayout mTileLayout;
private float mSquishinessFraction = 1f;
private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>();
+ private final Rect mClippingRect = new Rect();
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -133,8 +135,7 @@
mHorizontalContentContainer = new RemeasuringLinearLayout(mContext);
mHorizontalContentContainer.setOrientation(LinearLayout.VERTICAL);
- mHorizontalContentContainer.setClipChildren(true);
- mHorizontalContentContainer.setClipToPadding(false);
+ setHorizontalContentContainerClipping();
LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
int marginSize = (int) mContext.getResources().getDimension(R.dimen.qs_media_padding);
@@ -148,6 +149,23 @@
}
}
+ protected void setHorizontalContentContainerClipping() {
+ mHorizontalContentContainer.setClipChildren(true);
+ mHorizontalContentContainer.setClipToPadding(false);
+ // Don't clip on the top, that way, secondary pages tiles can animate up
+ mHorizontalContentContainer.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ if (left != oldLeft || right != oldRight || bottom != oldBottom) {
+ mClippingRect.left = left;
+ mClippingRect.right = right;
+ mClippingRect.bottom = bottom;
+ mHorizontalContentContainer.setClipBounds(mClippingRect);
+ }
+ });
+ mClippingRect.top = -1000;
+ mHorizontalContentContainer.setClipBounds(mClippingRect);
+ }
+
/**
* Add brightness view above the tile layout.
*
@@ -558,14 +576,6 @@
fireScanStateChanged(tileRecord.scanState);
}
}
-
- @Override
- public void onAnnouncementRequested(CharSequence announcement) {
- if (announcement != null) {
- mHandler.obtainMessage(H.ANNOUNCE_FOR_ACCESSIBILITY, announcement)
- .sendToTarget();
- }
- }
};
tileRecord.tile.addCallback(callback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index d470fa2..ff9790c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -45,6 +45,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -258,6 +259,15 @@
return null;
}
+ QSTileView getTileView(String spec) {
+ for (QSPanelControllerBase.TileRecord r : mRecords) {
+ if (Objects.equals(r.tile.getTileSpec(), spec)) {
+ return r.tileView;
+ }
+ }
+ return null;
+ }
+
private String getTilesSpecs() {
return mRecords.stream()
.map(tileRecord -> tileRecord.tile.getTileSpec())
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 613e7f8..f5ae019 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -46,11 +46,9 @@
}
@Override
- void initialize() {
- super.initialize();
- if (mHorizontalContentContainer != null) {
- mHorizontalContentContainer.setClipChildren(false);
- }
+ protected void setHorizontalContentContainerClipping() {
+ mHorizontalContentContainer.setClipToPadding(false);
+ mHorizontalContentContainer.setClipChildren(false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index d4350f1..eae2565 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -212,9 +212,6 @@
@Override
public void onScanStateChanged(boolean state) {}
-
- @Override
- public void onAnnouncementRequested(CharSequence announcement) {}
}
private void addPackageTiles(final QSTileHost host) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index c2a9e3a..76950d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -32,6 +32,7 @@
import com.android.systemui.qs.tiles.CameraToggleTile;
import com.android.systemui.qs.tiles.CastTile;
import com.android.systemui.qs.tiles.CellularTile;
+import com.android.systemui.qs.tiles.ColorCorrectionTile;
import com.android.systemui.qs.tiles.ColorInversionTile;
import com.android.systemui.qs.tiles.DataSaverTile;
import com.android.systemui.qs.tiles.DeviceControlsTile;
@@ -71,6 +72,7 @@
private final Provider<BluetoothTile> mBluetoothTileProvider;
private final Provider<CellularTile> mCellularTileProvider;
private final Provider<DndTile> mDndTileProvider;
+ private final Provider<ColorCorrectionTile> mColorCorrectionTileProvider;
private final Provider<ColorInversionTile> mColorInversionTileProvider;
private final Provider<AirplaneModeTile> mAirplaneModeTileProvider;
private final Provider<WorkModeTile> mWorkModeTileProvider;
@@ -133,7 +135,8 @@
Provider<QuickAccessWalletTile> quickAccessWalletTileProvider,
Provider<QRCodeScannerTile> qrCodeScannerTileProvider,
Provider<OneHandedModeTile> oneHandedModeTileProvider,
- Provider<FgsManagerTile> fgsManagerTileProvider) {
+ Provider<FgsManagerTile> fgsManagerTileProvider,
+ Provider<ColorCorrectionTile> colorCorrectionTileProvider) {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
@@ -167,6 +170,7 @@
mQRCodeScannerTileProvider = qrCodeScannerTileProvider;
mOneHandedModeTileProvider = oneHandedModeTileProvider;
mFgsManagerTileProvider = fgsManagerTileProvider;
+ mColorCorrectionTileProvider = colorCorrectionTileProvider;
}
public final QSTile createTile(String tileSpec) {
@@ -239,6 +243,8 @@
return mOneHandedModeTileProvider.get();
case "fgsmanager":
return mFgsManagerTileProvider.get();
+ case "color_correction":
+ return mColorCorrectionTileProvider.get();
}
// Custom tiles
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 70e3a2b..b5f07d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -156,8 +156,14 @@
*
* Categories are defined in {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}
* by editing frameworks/base/proto/src/metrics_constants.proto.
+ *
+ * @deprecated Not needed as this logging is deprecated. Logging tiles is done using
+ * {@link QSTile#getMetricsSpec}
*/
- abstract public int getMetricsCategory();
+ @Deprecated
+ public int getMetricsCategory() {
+ return 0;
+ }
/**
* Performs initialization of the tile
@@ -445,27 +451,11 @@
}
private void handleStateChanged() {
- boolean delayAnnouncement = shouldAnnouncementBeDelayed();
if (mCallbacks.size() != 0) {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onStateChanged(mState);
}
- if (mAnnounceNextStateChange && !delayAnnouncement) {
- String announcement = composeChangeAnnouncement();
- if (announcement != null) {
- mCallbacks.get(0).onAnnouncementRequested(announcement);
- }
- }
}
- mAnnounceNextStateChange = mAnnounceNextStateChange && delayAnnouncement;
- }
-
- protected boolean shouldAnnouncementBeDelayed() {
- return false;
- }
-
- protected String composeChangeAnnouncement() {
- return null;
}
private void handleShowDetail(boolean show) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 6bb79867..7efb983 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -653,7 +653,8 @@
"qr_code_scanner" to R.array.tile_states_qr_code_scanner,
"alarm" to R.array.tile_states_alarm,
"onehanded" to R.array.tile_states_onehanded,
- "fgsmanager" to R.array.tile_states_fgsmanager
+ "fgsmanager" to R.array.tile_states_fgsmanager,
+ "color_correction" to R.array.tile_states_color_correction
)
fun getSubtitleId(spec: String?): Int {
@@ -665,4 +666,4 @@
return PropertyValuesHolder.ofInt(name, *values).apply {
setEvaluator(ArgbEvaluator.getInstance())
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 5650a17..ccec80d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -144,15 +144,6 @@
return MetricsEvent.QS_AIRPLANEMODE;
}
- @Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return mContext.getString(R.string.accessibility_quick_settings_airplane_changed_on);
- } else {
- return mContext.getString(R.string.accessibility_quick_settings_airplane_changed_off);
- }
- }
-
public void handleSetListening(boolean listening) {
super.handleSetListening(listening);
if (mListening == listening) return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 6fcfc3a..5552105 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -243,15 +243,6 @@
}
@Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_on);
- } else {
- return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_off);
- }
- }
-
- @Override
public boolean isAvailable() {
return mController.isBluetoothSupported();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 6e0ae29..76c84f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -287,15 +287,6 @@
return MetricsEvent.QS_CAST;
}
- @Override
- protected String composeChangeAnnouncement() {
- if (!mState.value) {
- // We only announce when it's turned off to avoid vocal overflow.
- return mContext.getString(R.string.accessibility_casting_turned_off);
- }
- return null;
- }
-
private String getDeviceName(CastDevice device) {
return device.name != null ? device.name
: mContext.getString(R.string.quick_settings_cast_device_default_name);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
new file mode 100644
index 0000000..6dfcf5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.service.quicksettings.Tile;
+import android.view.View;
+import android.widget.Switch;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.R.drawable;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
+
+import javax.inject.Inject;
+
+/** Quick settings tile: Color correction **/
+public class ColorCorrectionTile extends QSTileImpl<BooleanState> {
+
+ private final Icon mIcon = ResourceIcon.get(drawable.ic_qs_color_correction);
+ private final SettingObserver mSetting;
+
+ @Inject
+ public ColorCorrectionTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ UserTracker userTracker,
+ SecureSettings secureSettings
+ ) {
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+
+ mSetting = new SettingObserver(secureSettings, mHandler,
+ Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, userTracker.getUserId()) {
+ @Override
+ protected void handleValueChanged(int value, boolean observedChange) {
+ // mHandler is the background handler so calling this is OK
+ handleRefreshState(value);
+ }
+ };
+ }
+
+ @Override
+ protected void handleDestroy() {
+ super.handleDestroy();
+ mSetting.setListening(false);
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ public void handleSetListening(boolean listening) {
+ super.handleSetListening(listening);
+ mSetting.setListening(listening);
+ }
+
+ @Override
+ protected void handleUserSwitch(int newUserId) {
+ mSetting.setUserId(newUserId);
+ handleRefreshState(mSetting.getValue());
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return new Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS);
+ }
+
+ @Override
+ protected void handleClick(@Nullable View view) {
+ mSetting.setValue(mState.value ? 0 : 1);
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_color_correction_label);
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue();
+ final boolean enabled = value != 0;
+ state.value = enabled;
+ state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.label = mContext.getString(R.string.quick_settings_color_correction_label);
+ state.icon = mIcon;
+ state.expandedAccessibilityClassName = Switch.class.getName();
+ state.contentDescription = state.label;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // MetricsProto/MetricsEvent is deprecated, so just simply return 0 here.
+ return 0;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index a2577d6..1bbe411 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -138,15 +138,6 @@
}
@Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return mContext.getString(R.string.accessibility_quick_settings_data_saver_changed_on);
- } else {
- return mContext.getString(R.string.accessibility_quick_settings_data_saver_changed_off);
- }
- }
-
- @Override
public void onDataSaverChanged(boolean isDataSaving) {
refreshState(isDataSaving);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index bb27458..49c548d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -307,15 +307,6 @@
}
@Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return mContext.getString(R.string.accessibility_quick_settings_dnd_changed_on);
- } else {
- return mContext.getString(R.string.accessibility_quick_settings_dnd_changed_off);
- }
- }
-
- @Override
public void handleSetListening(boolean listening) {
super.handleSetListening(listening);
if (mListening == listening) return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index c0065a0..73b0896 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -153,15 +153,6 @@
}
@Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return mContext.getString(R.string.accessibility_quick_settings_flashlight_changed_on);
- } else {
- return mContext.getString(R.string.accessibility_quick_settings_flashlight_changed_off);
- }
- }
-
- @Override
public void onFlashlightChanged(boolean enabled) {
refreshState(enabled);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 87edc2c..d7aa8b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -195,15 +195,6 @@
return MetricsEvent.QS_HOTSPOT;
}
- @Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return mContext.getString(R.string.accessibility_quick_settings_hotspot_changed_on);
- } else {
- return mContext.getString(R.string.accessibility_quick_settings_hotspot_changed_off);
- }
- }
-
/**
* Listens to changes made to hotspot and data saver states (to toggle tile availability).
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 6a018f6..fc93f44 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -132,15 +132,6 @@
return MetricsEvent.QS_LOCATION;
}
- @Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return mContext.getString(R.string.accessibility_quick_settings_location_changed_on);
- } else {
- return mContext.getString(R.string.accessibility_quick_settings_location_changed_off);
- }
- }
-
private final class Callback implements LocationChangeCallback,
KeyguardStateController.Callback {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index cd2e27a..0886b46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -147,15 +147,6 @@
return MetricsEvent.QS_NFC;
}
- @Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return mContext.getString(R.string.quick_settings_nfc_on);
- } else {
- return mContext.getString(R.string.quick_settings_nfc_off);
- }
- }
-
private NfcAdapter getAdapter() {
if (mAdapter == null) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 082dc5c..177c82e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -203,11 +203,6 @@
return mContext.getString(R.string.accessibility_quick_settings_rotation);
}
- @Override
- protected String composeChangeAnnouncement() {
- return getAccessibilityString(mState.value);
- }
-
private final RotationLockControllerCallback mCallback = new RotationLockControllerCallback() {
@Override
public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index da0069f..1608248 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -254,20 +254,6 @@
}
@Override
- protected boolean shouldAnnouncementBeDelayed() {
- return mStateBeforeClick.value == mState.value;
- }
-
- @Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return mContext.getString(R.string.accessibility_quick_settings_wifi_changed_on);
- } else {
- return mContext.getString(R.string.accessibility_quick_settings_wifi_changed_off);
- }
- }
-
- @Override
public boolean isAvailable() {
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index c9c1a9b..a22fda7 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -93,6 +93,12 @@
}
@Override
+ protected void onPause() {
+ super.onPause();
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ }
+
+ @Override
protected void onStop() {
super.onStop();
MetricsLogger.hidden(this, MetricsEvent.BRIGHTNESS_DIALOG);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 43b3fb1..7cf0583 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -257,6 +257,30 @@
return !canRemoveImmediately(entry.getKey());
}
+ /**
+ * @param key
+ * @return true if the entry is pinned
+ */
+ public boolean isSticky(String key) {
+ AlertEntry alerting = mAlertEntries.get(key);
+ if (alerting != null) {
+ return alerting.isSticky();
+ }
+ return false;
+ }
+
+ /**
+ * @param key
+ * @return When a HUN entry should be removed in milliseconds from now
+ */
+ public long getEarliestRemovalTime(String key) {
+ AlertEntry alerting = mAlertEntries.get(key);
+ if (alerting != null) {
+ return Math.max(0, alerting.mEarliestRemovaltime - mClock.currentTimeMillis());
+ }
+ return 0;
+ }
+
@Override
public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
if (shouldExtend) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index d7b4738..8ce73d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -31,6 +31,7 @@
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED;
+import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
import android.animation.Animator;
@@ -78,6 +79,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardIndication;
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
+import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -172,6 +174,18 @@
return view == mIndicationArea;
}
};
+ private ScreenLifecycle mScreenLifecycle;
+ private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
+ @Override
+ public void onScreenTurnedOn() {
+ if (mMessageToShowOnScreenOn != null) {
+ showBiometricMessage(mMessageToShowOnScreenOn);
+ // We want to keep this message around in case the screen was off
+ hideBiometricMessageDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+ mMessageToShowOnScreenOn = null;
+ }
+ }
+ };
/**
* Creates a new KeyguardIndicationController and registers callbacks.
@@ -190,6 +204,7 @@
@Main DelayableExecutor executor,
FalsingManager falsingManager,
LockPatternUtils lockPatternUtils,
+ ScreenLifecycle screenLifecycle,
IActivityManager iActivityManager,
KeyguardBypassController keyguardBypassController) {
mContext = context;
@@ -208,7 +223,8 @@
mIActivityManager = iActivityManager;
mFalsingManager = falsingManager;
mKeyguardBypassController = keyguardBypassController;
-
+ mScreenLifecycle = screenLifecycle;
+ mScreenLifecycle.addObserver(mScreenObserver);
}
/** Call this after construction to finish setting up the instance. */
@@ -991,7 +1007,7 @@
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
mInitialTextColorState);
- } else if (mKeyguardUpdateMonitor.isScreenOn()) {
+ } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
if (biometricSourceType == BiometricSourceType.FACE
&& shouldSuppressFaceMsgAndShowTryFingerprintMsg()) {
showTryFingerprintMsg(msgId, helpString);
@@ -1013,7 +1029,7 @@
if (biometricSourceType == BiometricSourceType.FACE
&& shouldSuppressFaceMsgAndShowTryFingerprintMsg()
&& !mStatusBarKeyguardViewManager.isBouncerShowing()
- && mKeyguardUpdateMonitor.isScreenOn()) {
+ && mScreenLifecycle.getScreenState() == SCREEN_ON) {
showTryFingerprintMsg(msgId, errString);
return;
}
@@ -1035,7 +1051,7 @@
}
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
- } else if (mKeyguardUpdateMonitor.isScreenOn()) {
+ } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
showBiometricMessage(errString);
} else {
mMessageToShowOnScreenOn = errString;
@@ -1086,16 +1102,6 @@
}
@Override
- public void onScreenTurnedOn() {
- if (mMessageToShowOnScreenOn != null) {
- showBiometricMessage(mMessageToShowOnScreenOn);
- // We want to keep this message around in case the screen was off
- hideBiometricMessageDelayed(HIDE_DELAY_MS);
- mMessageToShowOnScreenOn = null;
- }
- }
-
- @Override
public void onBiometricRunningStateChanged(boolean running,
BiometricSourceType biometricSourceType) {
if (running && biometricSourceType == BiometricSourceType.FACE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 2dbe59e..bd948ece 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -28,6 +28,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.SystemProperties;
+import android.os.Trace;
import android.text.format.DateFormat;
import android.util.FloatProperty;
import android.util.Log;
@@ -193,6 +194,7 @@
mState = state;
mUpcomingState = state;
mUiEventLogger.log(StatusBarStateEvent.fromState(mState));
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", "StatusBarState " + tag);
for (RankedListener rl : new ArrayList<>(mListeners)) {
rl.mListener.onStateChanged(mState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 22c3eda..9da7d21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -16,7 +16,8 @@
class NotificationLaunchAnimatorControllerProvider(
private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
private val notificationListContainer: NotificationListContainer,
- private val headsUpManager: HeadsUpManagerPhone
+ private val headsUpManager: HeadsUpManagerPhone,
+ private val jankMonitor: InteractionJankMonitor
) {
fun getAnimatorController(
notification: ExpandableNotificationRow
@@ -25,7 +26,8 @@
notificationShadeWindowViewController,
notificationListContainer,
headsUpManager,
- notification
+ notification,
+ jankMonitor
)
}
}
@@ -39,7 +41,8 @@
private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
private val notificationListContainer: NotificationListContainer,
private val headsUpManager: HeadsUpManagerPhone,
- private val notification: ExpandableNotificationRow
+ private val notification: ExpandableNotificationRow,
+ private val jankMonitor: InteractionJankMonitor
) : ActivityLaunchAnimator.Controller {
companion object {
@@ -137,12 +140,12 @@
notification.isExpandAnimationRunning = true
notificationListContainer.setExpandingNotification(notification)
- InteractionJankMonitor.getInstance().begin(notification,
+ jankMonitor.begin(notification,
InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
- InteractionJankMonitor.getInstance().end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
+ jankMonitor.end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
notification.isExpandAnimationRunning = false
notificationShadeWindowViewController.setExpandAnimationRunning(false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
index f8b4274..ed9663e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -19,9 +19,12 @@
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
import static com.android.systemui.statusbar.notification.interruption.HeadsUpController.alertAgain;
+import android.util.ArraySet;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -38,8 +41,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-
-import java.util.Objects;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import javax.inject.Inject;
@@ -66,12 +68,11 @@
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final NotificationRemoteInputManager mRemoteInputManager;
private final NodeController mIncomingHeaderController;
-
- // tracks the current HeadUpNotification reported by HeadsUpManager
- private @Nullable NotificationEntry mCurrentHun;
+ private final DelayableExecutor mExecutor;
private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
- private NotificationEntry mNotifExtendingLifetime; // notif we've extended the lifetime for
+ // notifs we've extended the lifetime for
+ private final ArraySet<NotificationEntry> mNotifsExtendingLifetime = new ArraySet<>();
@Inject
public HeadsUpCoordinator(
@@ -79,12 +80,14 @@
HeadsUpViewBinder headsUpViewBinder,
NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationRemoteInputManager remoteInputManager,
- @IncomingHeader NodeController incomingHeaderController) {
+ @IncomingHeader NodeController incomingHeaderController,
+ @Main DelayableExecutor executor) {
mHeadsUpManager = headsUpManager;
mHeadsUpViewBinder = headsUpViewBinder;
mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mRemoteInputManager = remoteInputManager;
mIncomingHeaderController = incomingHeaderController;
+ mExecutor = executor;
}
@Override
@@ -178,16 +181,26 @@
public boolean shouldExtendLifetime(@NonNull NotificationEntry entry, int reason) {
boolean isShowingHun = isCurrentlyShowingHun(entry);
if (isShowingHun) {
- mNotifExtendingLifetime = entry;
+ if (isSticky(entry)) {
+ long removeAfterMillis = mHeadsUpManager.getEarliestRemovalTime(entry.getKey());
+ if (removeAfterMillis <= 0) return false;
+ mExecutor.executeDelayed(() -> {
+ // make sure that the entry was not updated
+ long removeAfterMillis2 =
+ mHeadsUpManager.getEarliestRemovalTime(entry.getKey());
+ if (mNotifsExtendingLifetime.contains(entry) && removeAfterMillis2 <= 0) {
+ mHeadsUpManager.removeNotification(entry.getKey(), true);
+ }
+ }, removeAfterMillis);
+ }
+ mNotifsExtendingLifetime.add(entry);
}
return isShowingHun;
}
@Override
public void cancelLifetimeExtension(@NonNull NotificationEntry entry) {
- if (Objects.equals(mNotifExtendingLifetime, entry)) {
- mNotifExtendingLifetime = null;
- }
+ mNotifsExtendingLifetime.remove(entry);
}
};
@@ -220,27 +233,24 @@
new OnHeadsUpChangedListener() {
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- NotificationEntry newHUN = mHeadsUpManager.getTopEntry();
- if (!Objects.equals(mCurrentHun, newHUN)) {
- mCurrentHun = newHUN;
- endNotifLifetimeExtension();
- }
if (!isHeadsUp) {
mHeadsUpViewBinder.unbindHeadsUpView(entry);
+ endNotifLifetimeExtensionIfExtended(entry);
}
}
};
- private boolean isCurrentlyShowingHun(ListEntry entry) {
- return mCurrentHun == entry.getRepresentativeEntry();
+ private boolean isSticky(NotificationEntry entry) {
+ return mHeadsUpManager.isSticky(entry.getKey());
}
- private void endNotifLifetimeExtension() {
- if (mNotifExtendingLifetime != null) {
- mEndLifetimeExtension.onEndLifetimeExtension(
- mLifetimeExtender,
- mNotifExtendingLifetime);
- mNotifExtendingLifetime = null;
+ private boolean isCurrentlyShowingHun(ListEntry entry) {
+ return mHeadsUpManager.isAlerting(entry.getKey());
+ }
+
+ private void endNotifLifetimeExtensionIfExtended(NotificationEntry entry) {
+ if (mNotifsExtendingLifetime.remove(entry)) {
+ mEndLifetimeExtension.onEndLifetimeExtension(mLifetimeExtender, entry);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index ab36da5..6218c77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -520,26 +520,66 @@
}
/**
+ * Called when removing a view from its transient container, such as at the end of an animation.
+ * Generally, when operating on ExpandableView instances, this should be used rather than
+ * {@link ExpandableView#removeTransientView(View)} to ensure that the
+ * {@link #getTransientContainer() transient container} is correctly reset.
+ */
+ public void removeFromTransientContainer() {
+ final ViewGroup transientContainer = getTransientContainer();
+ if (transientContainer == null) {
+ return;
+ }
+ final ViewParent parent = getParent();
+ if (parent != transientContainer) {
+ Log.w(TAG, "Expandable view " + this
+ + " has transient container " + transientContainer
+ + " but different parent " + parent);
+ setTransientContainer(null);
+ return;
+ }
+ transientContainer.removeTransientView(this);
+ setTransientContainer(null);
+ }
+
+ /**
* Called before adding this view to a group, which would always throw an exception if this view
- * has a parent, so clean up the transient container and throw an exception if the parent isn't
- * a transient container. Provide as much detail in the event of a crash as possible.
+ * has a different parent, so clean up the transient container and throw an exception if the
+ * parent isn't a transient container. Provide as much detail as possible in the crash.
*/
public void removeFromTransientContainerForAdditionTo(ViewGroup newParent) {
final ViewParent parent = getParent();
+ final ViewGroup transientContainer = getTransientContainer();
if (parent == null) {
- // If this view has no parent, the add will succeed, so do nothing.
+ // If this view has no parent, the add will succeed, so just make sure the tracked
+ // transient container is in sync with the lack of a parent.
+ if (transientContainer != null) {
+ Log.w(TAG, "Expandable view " + this
+ + " has transient container " + transientContainer
+ + " but no parent");
+ setTransientContainer(null);
+ }
return;
}
- ViewGroup transientContainer = getTransientContainer();
if (transientContainer == null) {
throw new IllegalStateException(
"Can't add view " + this + " to container " + newParent + "; current parent "
+ parent + " is not a transient container");
}
if (transientContainer != parent) {
- throw new IllegalStateException(
- "Expandable view " + this + " has transient container " + transientContainer
- + " which is not the same as its parent " + parent);
+ String transientContainerOutOfSyncError = "Expandable view " + this
+ + " has transient container " + transientContainer
+ + " but different parent " + parent;
+ if (parent != newParent) {
+ // Crash with details before addView() crashes without any; the view is being added
+ // to a different parent, and the transient container isn't the parent, so we can't
+ // even (safely) clean that up.
+ throw new IllegalStateException(transientContainerOutOfSyncError);
+ } else {
+ Log.w(TAG, transientContainerOutOfSyncError);
+ setTransientContainer(null);
+ return;
+ }
}
if (parent != newParent) {
Log.w(TAG, "Moving view " + this + " from transient container "
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 88198f3..1d90780 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -215,8 +215,7 @@
// TODO: We should really cancel the active animations here. This will
// happen automatically when the view's intro animation starts, but
// it's a fragile link.
- header.transientContainer?.removeTransientView(header)
- header.transientContainer = null
+ header.removeFromTransientContainer()
parent.addView(header, target)
} else {
parent.changeViewPosition(header, target)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 3e9ce25..943f05f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3213,10 +3213,7 @@
// Clean up any potential transient views if the child has already been swiped
// out, as we won't be animating it further (due to its height already being
// clipped to 0.
- ViewGroup transientContainer = child.getTransientContainer();
- if (transientContainer != null) {
- transientContainer.removeTransientView(child);
- }
+ child.removeFromTransientContainer();
}
}
int animationType = childWasSwipedOut
@@ -3933,7 +3930,11 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
- viewGroup.removeTransientView(viewGroup.getTransientView(0));
+ final View transientView = viewGroup.getTransientView(0);
+ viewGroup.removeTransientView(transientView);
+ if (transientView instanceof ExpandableView) {
+ ((ExpandableView) transientView).setTransientContainer(null);
+ }
}
}
@@ -4102,7 +4103,7 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void clearTransient() {
for (ExpandableView view : mClearTransientViewsWhenFinished) {
- StackStateAnimator.removeTransientView(view);
+ view.removeFromTransientContainer();
}
mClearTransientViewsWhenFinished.clear();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 41a80c7..fc612a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -183,6 +183,7 @@
private final NotificationGroupManagerLegacy mLegacyGroupManager;
private final SectionHeaderController mSilentHeaderController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final InteractionJankMonitor mJankMonitor;
private NotificationStackScrollLayout mView;
private boolean mFadeNotificationsOnDismiss;
@@ -454,10 +455,7 @@
if (!row.isDismissed()) {
handleChildViewDismissed(view);
}
- ViewGroup transientContainer = row.getTransientContainer();
- if (transientContainer != null) {
- transientContainer.removeTransientView(view);
- }
+ row.removeFromTransientContainer();
}
/**
@@ -661,7 +659,8 @@
LayoutInflater layoutInflater,
NotificationRemoteInputManager remoteInputManager,
VisualStabilityManager visualStabilityManager,
- ShadeController shadeController) {
+ ShadeController shadeController,
+ InteractionJankMonitor jankMonitor) {
mAllowLongPress = allowLongPress;
mNotificationGutsManager = notificationGutsManager;
mVisibilityProvider = visibilityProvider;
@@ -684,6 +683,7 @@
mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
mStatusBar = statusBar;
mScrimController = scrimController;
+ mJankMonitor = jankMonitor;
groupManager.registerGroupExpansionChangeListener(
(changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
@@ -1784,9 +1784,9 @@
// We log any touches other than down, which will be captured by onTouchEvent.
// In the intercept we only start tracing when it's not a down (otherwise that down
// would be duplicated when intercepted).
- if (scrollWantsIt && ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
- InteractionJankMonitor.getInstance().begin(mView,
- CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ if (mJankMonitor != null && scrollWantsIt
+ && ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ mJankMonitor.begin(mView, CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
}
return swipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt;
}
@@ -1854,24 +1854,25 @@
}
private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
+ if (mJankMonitor == null) {
+ Log.w(TAG, "traceJankOnTouchEvent, mJankMonitor is null");
+ return;
+ }
// Handle interaction jank monitor cases.
switch (action) {
case MotionEvent.ACTION_DOWN:
if (scrollerWantsIt) {
- InteractionJankMonitor.getInstance()
- .begin(mView, CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ mJankMonitor.begin(mView, CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
}
break;
case MotionEvent.ACTION_UP:
if (scrollerWantsIt && !mView.isFlingAfterUpEvent()) {
- InteractionJankMonitor.getInstance()
- .end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ mJankMonitor.end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
}
break;
case MotionEvent.ACTION_CANCEL:
if (scrollerWantsIt) {
- InteractionJankMonitor.getInstance()
- .cancel(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ mJankMonitor.cancel(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 2dc9276..1d0d374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -322,9 +322,8 @@
private void onAnimationFinished() {
mHostLayout.onChildAnimationFinished();
- for (ExpandableView transientViewsToRemove : mTransientViewsToRemove) {
- transientViewsToRemove.getTransientContainer()
- .removeTransientView(transientViewsToRemove);
+ for (ExpandableView transientViewToRemove : mTransientViewsToRemove) {
+ transientViewToRemove.removeFromTransientContainer();
}
mTransientViewsToRemove.clear();
}
@@ -353,7 +352,7 @@
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
if (changingView.getVisibility() != View.VISIBLE) {
- removeTransientView(changingView);
+ changingView.removeFromTransientContainer();
continue;
}
@@ -390,12 +389,11 @@
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- 0, () -> removeTransientView(changingView), null);
+ 0, changingView::removeFromTransientContainer, null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
- if (mHostLayout.isFullySwipedOut(changingView)
- && changingView.getTransientContainer() != null) {
- changingView.getTransientContainer().removeTransientView(changingView);
+ if (mHostLayout.isFullySwipedOut(changingView)) {
+ changingView.removeFromTransientContainer();
}
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
@@ -425,7 +423,7 @@
mHostLayout.addTransientView(changingView, 0);
changingView.setTransientContainer(mHostLayout);
mTmpState.initFrom(changingView);
- endRunnable = () -> removeTransientView(changingView);
+ endRunnable = changingView::removeFromTransientContainer;
}
float targetLocation = 0;
boolean needsAnimation = true;
@@ -468,12 +466,6 @@
}
}
- public static void removeTransientView(ExpandableView viewToRemove) {
- if (viewToRemove.getTransientContainer() != null) {
- viewToRemove.getTransientContainer().removeTransientView(viewToRemove);
- }
- }
-
public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
final boolean isRubberbanded) {
final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index ad1c232..bfa4a24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -634,20 +634,22 @@
Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(UI_EVENT_LOGGER::log);
- long currUptimeMillis = SystemClock.uptimeMillis();
- if (currUptimeMillis - mLastFpFailureUptimeMillis < 2000) { // attempt within 2 seconds
- mNumConsecutiveFpFailures += 1;
- } else {
- mNumConsecutiveFpFailures = 1;
- }
- mLastFpFailureUptimeMillis = currUptimeMillis;
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT
+ && mUpdateMonitor.isUdfpsSupported()) {
+ long currUptimeMillis = SystemClock.uptimeMillis();
+ if (currUptimeMillis - mLastFpFailureUptimeMillis
+ < (mStatusBarStateController.isDozing() ? 3500 : 2000)) {
+ mNumConsecutiveFpFailures += 1;
+ } else {
+ mNumConsecutiveFpFailures = 1;
+ }
+ mLastFpFailureUptimeMillis = currUptimeMillis;
- if (biometricSourceType.equals(BiometricSourceType.FINGERPRINT)
- && mUpdateMonitor.isUdfpsSupported()
- && mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
- mKeyguardViewController.showBouncer(true);
- UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
- mNumConsecutiveFpFailures = 0;
+ if (mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
+ startWakeAndUnlock(MODE_SHOW_BOUNCER);
+ UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+ mNumConsecutiveFpFailures = 0;
+ }
}
cleanup();
}
@@ -668,7 +670,8 @@
&& mUpdateMonitor.isUdfpsSupported()
&& (mStatusBarStateController.getState() == StatusBarState.SHADE
|| mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED)) {
- mKeyguardViewController.showBouncer(true);
+ startWakeAndUnlock(MODE_SHOW_BOUNCER);
+ UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
}
cleanup();
}
@@ -735,6 +738,11 @@
pw.println(" BiometricUnlockController:");
pw.print(" mMode="); pw.println(mMode);
pw.print(" mWakeLock="); pw.println(mWakeLock);
+ if (mUpdateMonitor.isUdfpsSupported()) {
+ pw.print(" mNumConsecutiveFpFailures="); pw.println(mNumConsecutiveFpFailures);
+ pw.print(" time since last failure=");
+ pw.println(SystemClock.uptimeMillis() - mLastFpFailureUptimeMillis);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index a88a3b6..b35e684 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -254,7 +254,6 @@
private void setPulsing(boolean pulsing) {
mStatusBarKeyguardViewManager.setPulsing(pulsing);
- mKeyguardViewMediator.setPulsing(pulsing);
mNotificationPanel.setPulsing(pulsing);
mStatusBarStateController.setPulsing(pulsing);
mIgnoreTouchWhilePulsing = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
new file mode 100644
index 0000000..56b0d598
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback
+import com.android.internal.R
+
+/**
+ * Listens for fold state changes and reports the new folded state together with other properties
+ * associated with that state.
+ */
+internal class FoldStateListener(
+ context: Context,
+ private val listener: OnFoldStateChangeListener
+) : DeviceStateCallback {
+
+ internal interface OnFoldStateChangeListener {
+ /**
+ * Reports that the device either became folded or unfolded.
+ *
+ * @param folded whether the device is folded.
+ * @param willGoToSleep whether the device will go to sleep and keep the screen off.
+ */
+ fun onFoldStateChanged(folded: Boolean, willGoToSleep: Boolean)
+ }
+
+ private val foldedDeviceStates: IntArray =
+ context.resources.getIntArray(R.array.config_foldedDeviceStates)
+ private val goToSleepDeviceStates: IntArray =
+ context.resources.getIntArray(R.array.config_deviceStatesOnWhichToSleep)
+
+ private var wasFolded: Boolean? = null
+
+ override fun onStateChanged(state: Int) {
+ val isFolded = foldedDeviceStates.contains(state)
+ if (wasFolded == isFolded) {
+ return
+ }
+ wasFolded = isFolded
+ val willGoToSleep = goToSleepDeviceStates.contains(state)
+ listener.onFoldStateChanged(isFolded, willGoToSleep)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 9647486..565b2d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -33,7 +33,6 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.keyguard.KeyguardHostViewController;
-import com.android.keyguard.KeyguardRootViewController;
import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -42,7 +41,6 @@
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.DejankUtils;
import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -105,12 +103,11 @@
private int mStatusBarHeight;
private float mExpansion = EXPANSION_HIDDEN;
- protected ViewGroup mRoot;
- private KeyguardRootViewController mRootViewController;
private boolean mShowingSoon;
private int mBouncerPromptReason;
private boolean mIsAnimatingAway;
private boolean mIsScrimmed;
+ private boolean mInitialized;
private KeyguardBouncer(Context context, ViewMediatorCallback callback,
ViewGroup container,
@@ -184,7 +181,7 @@
showPrimarySecurityScreen();
}
- if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) {
+ if (mContainer.getVisibility() == View.VISIBLE || mShowingSoon) {
return;
}
@@ -235,10 +232,8 @@
Log.wtf(TAG, "onFullyShown when view was null");
} else {
mKeyguardViewController.onResume();
- if (mRoot != null) {
- mRoot.announceForAccessibility(
- mKeyguardViewController.getAccessibilityTitleForCurrentMode());
- }
+ mContainer.announceForAccessibility(
+ mKeyguardViewController.getAccessibilityTitleForCurrentMode());
}
}
@@ -253,10 +248,8 @@
}
private void setVisibility(@View.Visibility int visibility) {
- if (mRoot != null) {
- mRoot.setVisibility(visibility);
- dispatchVisibilityChanged();
- }
+ mContainer.setVisibility(visibility);
+ dispatchVisibilityChanged();
}
private final Runnable mShowRunnable = new Runnable() {
@@ -337,14 +330,12 @@
mKeyguardViewController.cleanUp();
}
mIsAnimatingAway = false;
- if (mRoot != null) {
- setVisibility(View.INVISIBLE);
- if (destroyView) {
+ setVisibility(View.INVISIBLE);
+ if (destroyView) {
- // We have a ViewFlipper that unregisters a broadcast when being detached, which may
- // be slow because of AM lock contention during unlocking. We can delay it a bit.
- mHandler.postDelayed(mRemoveViewRunnable, 50);
- }
+ // We have a ViewFlipper that unregisters a broadcast when being detached, which may
+ // be slow because of AM lock contention during unlocking. We can delay it a bit.
+ mHandler.postDelayed(mRemoveViewRunnable, 50);
}
}
@@ -370,14 +361,13 @@
}
public void onScreenTurnedOff() {
- if (mKeyguardViewController != null
- && mRoot != null && mRoot.getVisibility() == View.VISIBLE) {
+ if (mKeyguardViewController != null && mContainer.getVisibility() == View.VISIBLE) {
mKeyguardViewController.onPause();
}
}
public boolean isShowing() {
- return (mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE))
+ return (mShowingSoon || mContainer.getVisibility() == View.VISIBLE)
&& mExpansion == EXPANSION_VISIBLE && !isAnimatingAway();
}
@@ -401,7 +391,7 @@
}
public void prepare() {
- boolean wasInitialized = mRoot != null;
+ boolean wasInitialized = mInitialized;
ensureView();
if (wasInitialized) {
showPrimarySecurityScreen();
@@ -461,7 +451,7 @@
// in this case we need to force the removal, otherwise we'll
// end up in an unpredictable state.
boolean forceRemoval = mHandler.hasCallbacks(mRemoveViewRunnable);
- if (mRoot == null || forceRemoval) {
+ if (!mInitialized || forceRemoval) {
inflateView();
}
}
@@ -469,28 +459,24 @@
protected void inflateView() {
removeView();
mHandler.removeCallbacks(mRemoveViewRunnable);
- KeyguardBouncerComponent component = mKeyguardBouncerComponentFactory.create();
- mRootViewController = component.getKeyguardRootViewController();
- mRootViewController.init();
- mRoot = mRootViewController.getView(); // TODO(b/166448040): Don't access root view here.
+
+ KeyguardBouncerComponent component = mKeyguardBouncerComponentFactory.create(mContainer);
mKeyguardViewController = component.getKeyguardHostViewController();
mKeyguardViewController.init();
- mContainer.addView(mRoot, mContainer.getChildCount());
mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
setVisibility(View.INVISIBLE);
- final WindowInsets rootInsets = mRoot.getRootWindowInsets();
+ final WindowInsets rootInsets = mContainer.getRootWindowInsets();
if (rootInsets != null) {
- mRoot.dispatchApplyWindowInsets(rootInsets);
+ mContainer.dispatchApplyWindowInsets(rootInsets);
}
+ mInitialized = true;
}
protected void removeView() {
- if (mRoot != null && mRoot.getParent() == mContainer) {
- mContainer.removeView(mRoot);
- mRoot = null;
- }
+ mContainer.removeAllViews();
+ mInitialized = false;
}
/**
@@ -577,7 +563,7 @@
private void dispatchVisibilityChanged() {
for (BouncerExpansionCallback callback : mExpansionCallbacks) {
- callback.onVisibilityChanged(mRoot.getVisibility() == View.VISIBLE);
+ callback.onVisibilityChanged(mContainer.getVisibility() == View.VISIBLE);
}
}
@@ -601,6 +587,7 @@
pw.println(" mShowingSoon: " + mShowingSoon);
pw.println(" mBouncerPromptReason: " + mBouncerPromptReason);
pw.println(" mIsAnimatingAway: " + mIsAnimatingAway);
+ pw.println(" mInitialized: " + mInitialized);
}
/** Update keyguard position based on a tapped X coordinate. */
@@ -675,7 +662,10 @@
mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
}
- public KeyguardBouncer create(@RootView ViewGroup container,
+ /**
+ * Construct a KeyguardBouncer that will exist in the given container.
+ */
+ public KeyguardBouncer create(ViewGroup container,
BouncerExpansionCallback expansionCallback) {
return new KeyguardBouncer(mContext, mCallback, container,
mDismissCallbackRegistry, mFalsingCollector, expansionCallback,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 7ca8652..732e5f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -57,8 +57,7 @@
private int mUserSwitchPreferredY;
/**
- * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
- * avatar.
+ * Minimum top margin to avoid overlap with status bar or multi-user switcher avatar.
*/
private int mMinTopMargin;
@@ -203,7 +202,7 @@
if (mBypassEnabled) {
return mUnlockedStackScrollerPadding;
} else if (mIsSplitShade) {
- return getClockY(1.0f, mDarkAmount);
+ return getClockY(1.0f, mDarkAmount) + mUserSwitchHeight;
} else {
return getClockY(1.0f, mDarkAmount) + mKeyguardStatusHeight;
}
@@ -213,7 +212,7 @@
if (mBypassEnabled) {
return (int) (mUnlockedStackScrollerPadding + mOverStretchAmount);
} else if (mIsSplitShade) {
- return Math.max(0, clockYPosition - mSplitShadeTopNotificationsMargin);
+ return clockYPosition - mSplitShadeTopNotificationsMargin + mUserSwitchHeight;
} else {
return clockYPosition + mKeyguardStatusHeight;
}
@@ -223,7 +222,7 @@
if (mBypassEnabled) {
return mUnlockedStackScrollerPadding;
} else if (mIsSplitShade) {
- return Math.max(mSplitShadeTargetTopMargin, mMinTopMargin);
+ return mSplitShadeTargetTopMargin + mUserSwitchHeight;
} else {
return mMinTopMargin + mKeyguardStatusHeight;
}
@@ -231,7 +230,7 @@
private int getExpandedPreferredClockY() {
if (mIsSplitShade) {
- return Math.max(mSplitShadeTargetTopMargin, mMinTopMargin);
+ return mSplitShadeTargetTopMargin;
} else {
return mMinTopMargin;
}
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 434671c..03b1627 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.view.View.GONE;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -197,6 +198,7 @@
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.Utils;
import com.android.systemui.util.settings.SecureSettings;
@@ -732,7 +734,9 @@
NotificationEntryManager notificationEntryManager,
CommunalStateController communalStateController,
KeyguardStateController keyguardStateController,
- StatusBarStateController statusBarStateController, DozeLog dozeLog,
+ StatusBarStateController statusBarStateController,
+ StatusBarWindowStateController statusBarWindowStateController,
+ DozeLog dozeLog,
DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper,
LatencyTracker latencyTracker, PowerManager powerManager,
AccessibilityManager accessibilityManager, @DisplayId int displayId,
@@ -862,6 +866,7 @@
mQs.animateHeaderSlidingOut();
}
});
+ statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
mThemeResId = mView.getContext().getThemeResId();
mKeyguardBypassController = bypassController;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -1440,8 +1445,11 @@
mKeyguardStatusViewController.displayClock(LARGE, shouldAnimateClockChange);
}
updateKeyguardStatusViewAlignment(true /* animate */);
- int userIconHeight = mKeyguardQsUserSwitchController != null
+ int userSwitcherHeight = mKeyguardQsUserSwitchController != null
? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
+ if (mKeyguardUserSwitcherController != null) {
+ userSwitcherHeight = mKeyguardUserSwitcherController.getHeight();
+ }
float expandedFraction =
mScreenOffAnimationController.shouldExpandNotifications()
? 1.0f : getExpandedFraction();
@@ -1461,7 +1469,7 @@
mStatusBarHeaderHeightKeyguard,
expandedFraction,
mKeyguardStatusViewController.getLockscreenHeight(),
- userIconHeight,
+ userSwitcherHeight,
userSwitcherPreferredY,
darkamount, mOverStretchAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
@@ -5006,4 +5014,14 @@
public PhoneStatusBarView.TouchEventHandler getStatusBarTouchEventHandler() {
return mStatusBarViewTouchEventHandler;
}
+
+ private void onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState int state) {
+ if (state != WINDOW_STATE_SHOWING
+ && mStatusBarStateController.getState() == StatusBarState.SHADE) {
+ collapsePanel(
+ false /* animate */,
+ false /* delayed */,
+ 1.0f /* speedUpFactor */);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index 72f1695..fb0e306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -50,7 +50,7 @@
import android.widget.FrameLayout;
import com.android.internal.view.FloatingActionMode;
-import com.android.internal.widget.FloatingToolbar;
+import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import com.android.systemui.R;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 81d4bbf..4e2eb6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -16,10 +16,7 @@
package com.android.systemui.statusbar.phone;
-import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-
import android.app.StatusBarManager;
-import android.graphics.RectF;
import android.hardware.display.AmbientDisplayConfiguration;
import android.media.AudioManager;
import android.media.session.MediaSessionLegacyHelper;
@@ -39,24 +36,15 @@
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dock.DockManager;
-import com.android.systemui.doze.DozeLog;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.tuner.TunerService;
import java.io.FileDescriptor;
@@ -69,27 +57,16 @@
*/
public class NotificationShadeWindowViewController {
private static final String TAG = "NotifShadeWindowVC";
- private final NotificationWakeUpCoordinator mCoordinator;
- private final PulseExpansionHandler mPulseExpansionHandler;
- private final DynamicPrivacyController mDynamicPrivacyController;
- private final KeyguardBypassController mBypassController;
- private final PluginManager mPluginManager;
private final FalsingCollector mFalsingCollector;
private final TunerService mTunerService;
- private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
- private final NotificationEntryManager mNotificationEntryManager;
- private final KeyguardStateController mKeyguardStateController;
private final SysuiStatusBarStateController mStatusBarStateController;
- private final DozeLog mDozeLog;
- private final DozeParameters mDozeParameters;
- private final CommandQueue mCommandQueue;
private final NotificationShadeWindowView mView;
- private final ShadeController mShadeController;
private final NotificationShadeDepthController mDepthController;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final LockIconViewController mLockIconViewController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final StatusBarWindowStateController mStatusBarWindowStateController;
private GestureDetector mGestureDetector;
private View mBrightnessMirror;
@@ -97,7 +74,7 @@
private boolean mTouchCancelled;
private boolean mExpandAnimationRunning;
private NotificationStackScrollLayout mStackScrollLayout;
- private PhoneStatusBarView mStatusBarView;
+ private PhoneStatusBarViewController mStatusBarViewController;
private StatusBar mService;
private NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
@@ -107,64 +84,36 @@
private final DockManager mDockManager;
private final NotificationPanelViewController mNotificationPanelViewController;
private final PanelExpansionStateManager mPanelExpansionStateManager;
- private final StatusBarWindowController mStatusBarWindowController;
- // Used for determining view / touch intersection
- private int[] mTempLocation = new int[2];
- private RectF mTempRect = new RectF();
private boolean mIsTrackingBarGesture = false;
@Inject
public NotificationShadeWindowViewController(
- NotificationWakeUpCoordinator coordinator,
- PulseExpansionHandler pulseExpansionHandler,
- DynamicPrivacyController dynamicPrivacyController,
- KeyguardBypassController bypassController,
LockscreenShadeTransitionController transitionController,
FalsingCollector falsingCollector,
- PluginManager pluginManager,
TunerService tunerService,
- NotificationLockscreenUserManager notificationLockscreenUserManager,
- NotificationEntryManager notificationEntryManager,
- KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController,
- DozeLog dozeLog,
- DozeParameters dozeParameters,
- CommandQueue commandQueue,
- ShadeController shadeController,
DockManager dockManager,
NotificationShadeDepthController depthController,
NotificationShadeWindowView notificationShadeWindowView,
NotificationPanelViewController notificationPanelViewController,
PanelExpansionStateManager panelExpansionStateManager,
- StatusBarWindowController statusBarWindowController,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ StatusBarWindowStateController statusBarWindowStateController,
LockIconViewController lockIconViewController) {
- mCoordinator = coordinator;
- mPulseExpansionHandler = pulseExpansionHandler;
- mDynamicPrivacyController = dynamicPrivacyController;
- mBypassController = bypassController;
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
- mPluginManager = pluginManager;
mTunerService = tunerService;
- mNotificationLockscreenUserManager = notificationLockscreenUserManager;
- mNotificationEntryManager = notificationEntryManager;
- mKeyguardStateController = keyguardStateController;
mStatusBarStateController = statusBarStateController;
- mDozeLog = dozeLog;
- mDozeParameters = dozeParameters;
- mCommandQueue = commandQueue;
mView = notificationShadeWindowView;
- mShadeController = shadeController;
mDockManager = dockManager;
mNotificationPanelViewController = notificationPanelViewController;
mPanelExpansionStateManager = panelExpansionStateManager;
mDepthController = depthController;
- mStatusBarWindowController = statusBarWindowController;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mStatusBarWindowStateController = statusBarWindowStateController;
mLockIconViewController = lockIconViewController;
// This view is not part of the newly inflated expanded status bar.
@@ -225,7 +174,7 @@
mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() {
@Override
public Boolean handleDispatchTouchEvent(MotionEvent ev) {
- if (mStatusBarView == null) {
+ if (mStatusBarViewController == null) { // Fix for b/192490822
Log.w(TAG, "Ignoring touch while statusBarView not yet set.");
return false;
}
@@ -247,11 +196,11 @@
}
if (isDown) {
- setTouchActive(true);
+ mTouchActive = true;
mTouchCancelled = false;
} else if (ev.getActionMasked() == MotionEvent.ACTION_UP
|| ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
- setTouchActive(false);
+ mTouchActive = false;
}
if (mTouchCancelled || mExpandAnimationRunning) {
return false;
@@ -293,27 +242,27 @@
expandingBelowNotch = true;
}
if (expandingBelowNotch) {
- return mStatusBarView.dispatchTouchEvent(ev);
+ return mStatusBarViewController.sendTouchToView(ev);
}
if (!mIsTrackingBarGesture && isDown
&& mNotificationPanelViewController.isFullyCollapsed()) {
float x = ev.getRawX();
float y = ev.getRawY();
- if (isIntersecting(mStatusBarView, x, y)) {
- if (mService.isSameStatusBarState(WINDOW_STATE_SHOWING)) {
+ if (mStatusBarViewController.touchIsWithinView(x, y)) {
+ if (mStatusBarWindowStateController.windowIsShowing()) {
mIsTrackingBarGesture = true;
- return mStatusBarView.dispatchTouchEvent(ev);
+ return mStatusBarViewController.sendTouchToView(ev);
} else { // it's hidden or hiding, don't send to notification shade.
return true;
}
}
} else if (mIsTrackingBarGesture) {
- final boolean sendToNotification = mStatusBarView.dispatchTouchEvent(ev);
+ final boolean sendToStatusBar = mStatusBarViewController.sendTouchToView(ev);
if (isUp || isCancel) {
mIsTrackingBarGesture = false;
}
- return sendToNotification;
+ return sendToStatusBar;
}
return null;
@@ -458,10 +407,6 @@
return mView;
}
- public void setTouchActive(boolean touchActive) {
- mTouchActive = touchActive;
- }
-
public void cancelCurrentTouch() {
if (mTouchActive) {
final long now = SystemClock.uptimeMillis();
@@ -496,8 +441,8 @@
}
}
- public void setStatusBarView(PhoneStatusBarView statusBarView) {
- mStatusBarView = statusBarView;
+ public void setStatusBarViewController(PhoneStatusBarViewController statusBarViewController) {
+ mStatusBarViewController = statusBarViewController;
}
public void setService(StatusBar statusBar, NotificationShadeWindowController controller) {
@@ -509,11 +454,4 @@
void setDragDownHelper(DragDownHelper dragDownHelper) {
mDragDownHelper = dragDownHelper;
}
-
- private boolean isIntersecting(View view, float x, float y) {
- mTempLocation = view.getLocationOnScreen();
- mTempRect.set(mTempLocation[0], mTempLocation[1], mTempLocation[0] + view.getWidth(),
- mTempLocation[1] + view.getHeight());
- return mTempRect.contains(x, y);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index b9386bd..1cb19ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -17,6 +17,7 @@
import android.content.res.Configuration
import android.graphics.Point
+import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
@@ -92,6 +93,30 @@
mView.importantForAccessibility = mode
}
+ /**
+ * Sends a touch event to the status bar view.
+ *
+ * This is required in certain cases because the status bar view is in a separate window from
+ * the rest of SystemUI, and other windows may decide that their touch should instead be treated
+ * as a status bar window touch.
+ */
+ fun sendTouchToView(ev: MotionEvent): Boolean {
+ return mView.dispatchTouchEvent(ev)
+ }
+
+ /**
+ * Returns true if the given (x, y) point (in screen coordinates) is within the status bar
+ * view's range and false otherwise.
+ */
+ fun touchIsWithinView(x: Float, y: Float): Boolean {
+ val left = mView.locationOnScreen[0]
+ val top = mView.locationOnScreen[1]
+ return left <= x &&
+ x <= left + mView.width &&
+ top <= y &&
+ y <= top + mView.height
+ }
+
class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
override fun getViewCenter(view: View, outPoint: Point) =
when (view.id) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index 2d41e5e..24bb7f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -55,6 +55,9 @@
*/
boolean closeShadeIfOpen();
+ /** Returns whether the shade is currently open or opening. */
+ boolean isShadeOpen();
+
/**
* Add a runnable for NotificationPanelView to post when the panel is expanded.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index b4fed2b..f8b0535 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -148,6 +148,13 @@
}
@Override
+ public boolean isShadeOpen() {
+ NotificationPanelViewController controller =
+ getNotificationPanelViewController();
+ return controller.isExpanding() || controller.isFullyExpanded();
+ }
+
+ @Override
public void postOnShadeExpanded(Runnable executable) {
getNotificationPanelViewController().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 2ba70df..1cc5e12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -70,6 +70,7 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.PointF;
+import android.hardware.devicestate.DeviceStateManager;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Bundle;
@@ -116,6 +117,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -232,6 +234,7 @@
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -337,15 +340,9 @@
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
- void setWindowState(int state) {
- mStatusBarWindowState = state;
- mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
- mStatusBarHideIconsForBouncerManager.setStatusBarWindowHidden(mStatusBarWindowHidden);
- if (mStatusBarView != null) {
- // Should #updateHideIconsForBouncer always be called, regardless of whether we have a
- // status bar view? If so, we can make #updateHideIconsForBouncer private.
- mStatusBarHideIconsForBouncerManager.updateHideIconsForBouncer(/* animate= */ false);
- }
+ void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
+ updateBubblesVisibility();
+ mStatusBarWindowState = state;
}
void acquireGestureWakeLock(long time) {
@@ -464,7 +461,7 @@
private PhoneStatusBarViewController mPhoneStatusBarViewController;
private PhoneStatusBarTransitions mStatusBarTransitions;
private AuthRippleController mAuthRippleController;
- private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
+ @WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
protected NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarWindowController mStatusBarWindowController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -677,6 +674,8 @@
private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
(extractor, which) -> updateTheme();
+ private final InteractionJankMonitor mJankMonitor;
+
/**
* Public constructor for StatusBar.
@@ -692,6 +691,7 @@
LightBarController lightBarController,
AutoHideController autoHideController,
StatusBarWindowController statusBarWindowController,
+ StatusBarWindowStateController statusBarWindowStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
StatusBarSignalPolicy statusBarSignalPolicy,
PulseExpansionHandler pulseExpansionHandler,
@@ -779,7 +779,9 @@
WallpaperManager wallpaperManager,
Optional<StartingSurface> startingSurfaceOptional,
ActivityLaunchAnimator activityLaunchAnimator,
- NotifPipelineFlags notifPipelineFlags) {
+ NotifPipelineFlags notifPipelineFlags,
+ InteractionJankMonitor jankMonitor,
+ DeviceStateManager deviceStateManager) {
super(context);
mNotificationsController = notificationsController;
mFragmentService = fragmentService;
@@ -867,11 +869,13 @@
mMainExecutor = delayableExecutor;
mMessageRouter = messageRouter;
mWallpaperManager = wallpaperManager;
+ mJankMonitor = jankMonitor;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
mNotifPipelineFlags = notifPipelineFlags;
lockscreenShadeTransitionController.setStatusbar(this);
+ statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
mScreenOffAnimationController = screenOffAnimationController;
@@ -900,6 +904,9 @@
data -> mCommandQueueCallbacks.animateExpandSettingsPanel(data.mSubpanel));
mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT,
id -> onLaunchTransitionTimeout());
+
+ deviceStateManager.registerCallback(mMainExecutor,
+ new FoldStateListener(mContext, this::onFoldedStateChanged));
}
@Override
@@ -1098,6 +1105,27 @@
requestTopUi, componentTag))));
}
+ private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) {
+ // Folded state changes are followed by a screen off event.
+ // By default turning off the screen also closes the shade.
+ // We want to make sure that the shade status is kept after
+ // folding/unfolding.
+ boolean isShadeOpen = mShadeController.isShadeOpen();
+ boolean leaveOpen = isShadeOpen && !willGoToSleep;
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "#onFoldedStateChanged(): "
+ + "isFolded=%s, "
+ + "willGoToSleep=%s, "
+ + "isShadeOpen=%s, "
+ + "leaveOpen=%s",
+ isFolded, willGoToSleep, isShadeOpen, leaveOpen));
+ }
+ if (leaveOpen) {
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+ }
+ }
+
// ================================================================================
// Constructing the view
// ================================================================================
@@ -1133,7 +1161,8 @@
mStatusBarView = statusBarView;
mPhoneStatusBarViewController = statusBarViewController;
mStatusBarTransitions = statusBarTransitions;
- mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
+ mNotificationShadeWindowViewController
+ .setStatusBarViewController(mPhoneStatusBarViewController);
// Ensure we re-propagate panel expansion values to the panel controller and
// any listeners it may have, such as PanelBar. This will also ensure we
// re-display the notification panel if necessary (for example, if
@@ -1387,7 +1416,8 @@
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
mNotificationShadeWindowViewController,
mStackScrollerController.getNotificationListContainer(),
- mHeadsUpManager
+ mHeadsUpManager,
+ mJankMonitor
);
// TODO: inject this.
@@ -2105,10 +2135,6 @@
}
}
- boolean isSameStatusBarState(int state) {
- return mStatusBarWindowState == state;
- }
-
public GestureRecorder getGestureRecorder() {
return mGestureRec;
}
@@ -3586,7 +3612,7 @@
final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
- public void onScreenTurningOn() {
+ public void onScreenTurningOn(Runnable onDrawn) {
mFalsingCollector.onScreenTurningOn();
mNotificationPanelViewController.onScreenTurningOn();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index b391de3..51f2e67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.app.StatusBarManager.windowStateToString;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.containsType;
@@ -58,7 +56,6 @@
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
-import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -515,30 +512,6 @@
}
@Override
- public void setWindowState(
- int displayId, @StatusBarManager.WindowType int window,
- @StatusBarManager.WindowVisibleState int state) {
- if (displayId != mDisplayId) {
- return;
- }
- boolean showing = state == WINDOW_STATE_SHOWING;
- if (mNotificationShadeWindowView != null
- && window == StatusBarManager.WINDOW_STATUS_BAR
- && !mStatusBar.isSameStatusBarState(state)) {
- mStatusBar.setWindowState(state);
- if (StatusBar.DEBUG_WINDOW_STATE) {
- Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state));
- }
- if (!showing && mStatusBarStateController.getState() == StatusBarState.SHADE) {
- mNotificationPanelViewController.collapsePanel(
- false /* animate */, false /* delayed */, 1.0f /* speedUpFactor */);
- }
- }
-
- mStatusBar.updateBubblesVisibility();
- }
-
- @Override
public void showAssistDisclosure() {
mAssistManager.showDisclosure();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
index d2181d0..17516e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
@@ -1,10 +1,12 @@
package com.android.systemui.statusbar.phone
+import android.app.StatusBarManager
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.FileDescriptor
import java.io.PrintWriter
@@ -25,6 +27,7 @@
class StatusBarHideIconsForBouncerManager @Inject constructor(
private val commandQueue: CommandQueue,
@Main private val mainExecutor: DelayableExecutor,
+ statusBarWindowStateController: StatusBarWindowStateController,
dumpManager: DumpManager
) : Dumpable {
// State variables set by external classes.
@@ -42,6 +45,9 @@
init {
dumpManager.registerDumpable(this)
+ statusBarWindowStateController.addListener {
+ state -> setStatusBarStateAndTriggerUpdate(state)
+ }
}
/** Returns true if the status bar icons should be hidden in the bouncer. */
@@ -49,8 +55,9 @@
return hideIconsForBouncer || wereIconsJustHidden
}
- fun setStatusBarWindowHidden(statusBarWindowHidden: Boolean) {
- this.statusBarWindowHidden = statusBarWindowHidden
+ private fun setStatusBarStateAndTriggerUpdate(@StatusBarManager.WindowVisibleState state: Int) {
+ statusBarWindowHidden = state == StatusBarManager.WINDOW_STATE_HIDDEN
+ updateHideIconsForBouncer(animate = false)
}
fun setDisplayId(displayId: Int) {
@@ -87,7 +94,7 @@
* Updates whether the status bar icons should be hidden in the bouncer. May trigger
* [commandQueue.recomputeDisableFlags] if the icon visibility status changes.
*/
- fun updateHideIconsForBouncer(animate: Boolean) {
+ private fun updateHideIconsForBouncer(animate: Boolean) {
val hideBecauseApp =
topAppHidesStatusBar &&
isOccluded &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 875b7e5..316e682 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -51,7 +51,6 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -87,7 +86,7 @@
public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener,
PanelExpansionListener, NavigationModeController.ModeChangedListener,
- KeyguardViewController, WakefulnessLifecycle.Observer {
+ KeyguardViewController {
// When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3;
@@ -392,17 +391,13 @@
} else {
mStatusBar.showKeyguard();
if (hideBouncerWhenShowing) {
- hideBouncer(shouldDestroyViewOnReset() /* destroyView */);
+ hideBouncer(false /* destroyView */);
mBouncer.prepare();
}
}
updateStates();
}
- protected boolean shouldDestroyViewOnReset() {
- return false;
- }
-
/**
* If applicable, shows the alternate authentication bouncer. Else, shows the input
* (pin/password/pattern) bouncer.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index f93a8dc..977fe9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -20,10 +20,12 @@
import android.app.WallpaperManager;
import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
import android.os.Handler;
import android.os.PowerManager;
import android.util.DisplayMetrics;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
@@ -105,6 +107,7 @@
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
@@ -140,6 +143,7 @@
LightBarController lightBarController,
AutoHideController autoHideController,
StatusBarWindowController statusBarWindowController,
+ StatusBarWindowStateController statusBarWindowStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
StatusBarSignalPolicy statusBarSignalPolicy,
PulseExpansionHandler pulseExpansionHandler,
@@ -227,7 +231,9 @@
WallpaperManager wallpaperManager,
Optional<StartingSurface> startingSurfaceOptional,
ActivityLaunchAnimator activityLaunchAnimator,
- NotifPipelineFlags notifPipelineFlags) {
+ NotifPipelineFlags notifPipelineFlags,
+ InteractionJankMonitor jankMonitor,
+ DeviceStateManager deviceStateManager) {
return new StatusBar(
context,
notificationsController,
@@ -235,6 +241,7 @@
lightBarController,
autoHideController,
statusBarWindowController,
+ statusBarWindowStateController,
keyguardUpdateMonitor,
statusBarSignalPolicy,
pulseExpansionHandler,
@@ -321,7 +328,9 @@
wallpaperManager,
startingSurfaceOptional,
activityLaunchAnimator,
- notifPipelineFlags
+ notifPipelineFlags,
+ jankMonitor,
+ deviceStateManager
);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 2dfcc98..c1f63b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -171,6 +171,8 @@
if (mShowing == showing && mOccluded == occluded) return;
mShowing = showing;
mOccluded = occluded;
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events",
+ "Keyguard showing: " + showing + " occluded: " + occluded);
notifyKeyguardChanged();
// Update the dismiss amount to the full 0f/1f if we explicitly show or hide the keyguard.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index ffa7963..04a6a11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -248,6 +248,10 @@
return mUserSwitcherController.isSimpleUserSwitcher();
}
+ public int getHeight() {
+ return mListView.getHeight();
+ }
+
/**
* @param animate if the transition should be animated
* @return true if the switcher state changed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
index cd8894c..850a4b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
@@ -97,9 +97,12 @@
} else {
// Update clickable state immediately so that the menu feels more responsive
userItemViews[i].setClickable(open);
- // Before running the animation, ensure visibility is set correctly
- userItemViews[i].updateVisibilities(animate || open /* showItem */,
- true /* showTextName */, false /* animate */);
+ // when opening we need to make views visible beforehand so they can be animated
+ if (open) {
+ userItemViews[i].updateVisibilities(true /* showItem */,
+ true /* showTextName */, false /* animate */);
+ }
+
}
}
@@ -117,6 +120,13 @@
setClipChildren(true);
setClipToPadding(true);
mAnimating = false;
+ if (!open) {
+ // after closing we hide children so that height of this view is correct
+ for (int i = 1; i < userItemViews.length; i++) {
+ userItemViews[i].updateVisibilities(false /* showItem */,
+ true /* showTextName */, false /* animate */);
+ }
+ }
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
new file mode 100644
index 0000000..3a14914
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.window
+
+import android.app.StatusBarManager
+import android.app.StatusBarManager.WindowVisibleState
+import android.app.StatusBarManager.WINDOW_STATE_SHOWING
+import android.app.StatusBarManager.WINDOW_STATUS_BAR
+import android.app.StatusBarManager.windowStateToString
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.DisplayId
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.phone.StatusBar
+import javax.inject.Inject
+
+/**
+ * A centralized class maintaining the state of the status bar window.
+ *
+ * Classes that want to get updates about the status bar window state should subscribe to this class
+ * via [addListener] and should NOT add their own callback on [CommandQueue].
+ */
+@SysUISingleton
+class StatusBarWindowStateController @Inject constructor(
+ @DisplayId private val thisDisplayId: Int,
+ commandQueue: CommandQueue
+) {
+ private val commandQueueCallback = object : CommandQueue.Callbacks {
+ override fun setWindowState(
+ displayId: Int,
+ @StatusBarManager.WindowType window: Int,
+ @WindowVisibleState state: Int
+ ) {
+ this@StatusBarWindowStateController.setWindowState(displayId, window, state)
+ }
+ }
+ private val listeners: MutableSet<StatusBarWindowStateListener> = HashSet()
+
+ @WindowVisibleState private var windowState: Int = WINDOW_STATE_SHOWING
+
+ init {
+ commandQueue.addCallback(commandQueueCallback)
+ }
+
+ /** Adds a listener. */
+ fun addListener(listener: StatusBarWindowStateListener) {
+ listeners.add(listener)
+ }
+
+ /** Returns true if the window is currently showing. */
+ fun windowIsShowing() = windowState == WINDOW_STATE_SHOWING
+
+ private fun setWindowState(
+ displayId: Int,
+ @StatusBarManager.WindowType window: Int,
+ @WindowVisibleState state: Int
+ ) {
+ if (displayId != thisDisplayId) {
+ return
+ }
+ if (window != WINDOW_STATUS_BAR) {
+ return
+ }
+ if (windowState == state) {
+ return
+ }
+
+ windowState = state
+ if (StatusBar.DEBUG_WINDOW_STATE) {
+ Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state))
+ }
+ listeners.forEach { it.onStatusBarWindowStateChanged(state) }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IResultCallback.aidl b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateListener.kt
similarity index 67%
copy from telephony/java/com/android/internal/telephony/euicc/IResultCallback.aidl
copy to packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateListener.kt
index 69f479c..f3bab04 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IResultCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateListener.kt
@@ -13,11 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.internal.telephony.euicc;
-import android.content.Intent;
+package com.android.systemui.statusbar.window
-/** @hide */
-oneway interface IResultCallback {
- void onComplete(int resultCode, in Intent resultIntent);
+import android.app.StatusBarManager
+
+/** Listener interface for changes in the status bar window state. */
+fun interface StatusBarWindowStateListener {
+ fun onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState state: Int)
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index fb9df01..52c416ba 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -19,7 +19,6 @@
import android.os.PowerManager
import android.provider.Settings
import com.android.systemui.keyguard.KeyguardViewMediator
-import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.ScreenOffAnimation
@@ -36,12 +35,10 @@
*/
@SysUIUnfoldScope
class FoldAodAnimationController @Inject constructor(
- private val screenLifecycle: ScreenLifecycle,
private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val globalSettings: GlobalSettings
-) : ScreenLifecycle.Observer,
- CallbackController<FoldAodAnimationStatus>,
+) : CallbackController<FoldAodAnimationStatus>,
ScreenOffAnimation,
WakefulnessLifecycle.Observer {
@@ -58,7 +55,6 @@
override fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
this.statusBar = statusBar
- screenLifecycle.addObserver(this)
wakefulnessLifecycle.addObserver(this)
}
@@ -123,7 +119,7 @@
}
}
- override fun onScreenTurnedOn() {
+ fun onScreenTurnedOn() {
if (shouldPlayAnimation) {
statusBar.notificationPanelViewController.startFoldToAodAnimation {
// End action
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index cd3e2d3..75dfd48 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -58,7 +58,8 @@
deviceStateManager,
sensorManager,
handler,
- executor
+ executor,
+ tracingTagPrefix = "systemui"
)
)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt b/packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt
similarity index 77%
rename from packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
rename to packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt
index a60033c..6cd384f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard
+package com.android.systemui.util.concurrency
import android.os.Trace
import java.util.concurrent.atomic.AtomicInteger
@@ -23,9 +23,9 @@
/**
* Allows to wait for multiple callbacks and notify when the last one is executed
*/
-class PendingDrawnTasksContainer {
+class PendingTasksContainer {
- private var pendingDrawnTasksCount: AtomicInteger = AtomicInteger(0)
+ private var pendingTasksCount: AtomicInteger = AtomicInteger(0)
private var completionCallback: AtomicReference<Runnable> = AtomicReference()
/**
@@ -33,19 +33,19 @@
* @return a runnable that should be invoked when the task is finished
*/
fun registerTask(name: String): Runnable {
- pendingDrawnTasksCount.incrementAndGet()
+ pendingTasksCount.incrementAndGet()
if (ENABLE_TRACE) {
- Trace.beginAsyncSection("PendingDrawnTasksContainer#$name", 0)
+ Trace.beginAsyncSection("PendingTasksContainer#$name", 0)
}
return Runnable {
- if (pendingDrawnTasksCount.decrementAndGet() == 0) {
+ if (pendingTasksCount.decrementAndGet() == 0) {
val onComplete = completionCallback.getAndSet(null)
onComplete?.run()
if (ENABLE_TRACE) {
- Trace.endAsyncSection("PendingDrawnTasksContainer#$name", 0)
+ Trace.endAsyncSection("PendingTasksContainer#$name", 0)
}
}
}
@@ -57,7 +57,7 @@
fun reset() {
// Create new objects in case if there are pending callbacks from the previous invocations
completionCallback = AtomicReference()
- pendingDrawnTasksCount = AtomicInteger(0)
+ pendingTasksCount = AtomicInteger(0)
}
/**
@@ -67,7 +67,7 @@
fun onTasksComplete(onComplete: Runnable) {
completionCallback.set(onComplete)
- if (pendingDrawnTasksCount.get() == 0) {
+ if (pendingTasksCount.get() == 0) {
val currentOnComplete = completionCallback.getAndSet(null)
currentOnComplete?.run()
}
@@ -76,7 +76,7 @@
/**
* Returns current pending tasks count
*/
- fun getPendingCount(): Int = pendingDrawnTasksCount.get()
+ fun getPendingCount(): Int = pendingTasksCount.get()
}
private const val ENABLE_TRACE = false
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
new file mode 100644
index 0000000..96e6bd1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard.mediator
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.unfold.FoldAodAnimationController
+import com.android.systemui.unfold.SysUIUnfoldComponent
+import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation
+import com.android.systemui.util.concurrency.FakeExecution
+import com.android.systemui.util.mockito.capture
+
+import java.util.Optional
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ScreenOnCoordinatorTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var runnable: Runnable
+ @Mock
+ private lateinit var unfoldComponent: SysUIUnfoldComponent
+ @Mock
+ private lateinit var foldAodAnimationController: FoldAodAnimationController
+ @Mock
+ private lateinit var unfoldAnimation: UnfoldLightRevealOverlayAnimation
+ @Mock
+ private lateinit var screenLifecycle: ScreenLifecycle
+ @Captor
+ private lateinit var readyCaptor: ArgumentCaptor<Runnable>
+
+ private lateinit var screenOnCoordinator: ScreenOnCoordinator
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(unfoldComponent.getUnfoldLightRevealOverlayAnimation())
+ .thenReturn(unfoldAnimation)
+ `when`(unfoldComponent.getFoldAodAnimationController())
+ .thenReturn(foldAodAnimationController)
+
+ screenOnCoordinator = ScreenOnCoordinator(
+ screenLifecycle,
+ Optional.of(unfoldComponent),
+ FakeExecution()
+ )
+
+ // Make sure screen events are registered to observe
+ verify(screenLifecycle).addObserver(screenOnCoordinator)
+ }
+
+ @Test
+ fun testUnfoldTransitionEnabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback() {
+ screenOnCoordinator.onScreenTurningOn(runnable)
+
+ onUnfoldOverlayReady()
+ onFoldAodReady()
+
+ // Should be called when both unfold overlay and keyguard drawn ready
+ verify(runnable).run()
+ }
+
+ @Test
+ fun testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback() {
+ // Recreate with empty unfoldComponent
+ screenOnCoordinator = ScreenOnCoordinator(
+ screenLifecycle,
+ Optional.empty(),
+ FakeExecution()
+ )
+ screenOnCoordinator.onScreenTurningOn(runnable)
+
+ // Should be called when only keyguard drawn
+ verify(runnable).run()
+ }
+
+ @Test
+ fun testWakeAndUnlockDelaysRunnable() {
+ // GIVEN wakeAndUnlocking has been set to true
+ screenOnCoordinator.wakeAndUnlocking = true
+
+ // WHEN the screen turns on and two tasks have completed
+ screenOnCoordinator.onScreenTurningOn(runnable)
+ onUnfoldOverlayReady()
+ onFoldAodReady()
+
+ // THEN the runnable should not have run yet
+ verify(runnable, never()).run()
+
+ // WHEN the value of wakeAndUnlocking changes
+ screenOnCoordinator.wakeAndUnlocking = false
+
+ // THEN the runnable should have run, as it is the last task to complete
+ verify(runnable).run()
+ }
+
+ private fun onUnfoldOverlayReady() {
+ verify(unfoldAnimation).onScreenTurningOn(capture(readyCaptor))
+ readyCaptor.getValue().run()
+ }
+
+ private fun onFoldAodReady() {
+ verify(foldAodAnimationController).onScreenTurningOn(capture(readyCaptor))
+ readyCaptor.getValue().run()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 4f3266d..40632a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -44,8 +44,10 @@
import com.android.systemui.statusbar.SmartReplyController;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Rule;
+import org.mockito.Mockito;
import java.io.FileInputStream;
import java.io.IOException;
@@ -120,11 +122,15 @@
TestableLooper.get(this).processAllMessages();
}
disallowTestableLooperAsMainThread();
- SystemUIFactory.cleanup();
mContext.cleanUpReceivers(this.getClass().getSimpleName());
mFakeBroadcastDispatcher.cleanUpReceivers(this.getClass().getSimpleName());
}
+ @AfterClass
+ public static void mockitoTearDown() {
+ Mockito.framework().clearInlineMocks();
+ }
+
/**
* Tests are run on the TestableLooper; however, there are parts of SystemUI that assert that
* the code is run from the main looper. Therefore, we allow the TestableLooper to pass these
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 27fcb11..9827d21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -34,7 +34,6 @@
import android.app.trust.TrustManager;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
-import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -42,10 +41,10 @@
import androidx.test.filters.SmallTest;
import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -58,9 +57,6 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.unfold.FoldAodAnimationController;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
-import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -69,13 +65,9 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
-import java.util.function.Function;
-
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -95,36 +87,25 @@
private @Mock NavigationModeController mNavigationModeController;
private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
private @Mock DozeParameters mDozeParameters;
- private @Mock SysUIUnfoldComponent mSysUIUnfoldComponent;
- private @Mock UnfoldLightRevealOverlayAnimation mUnfoldAnimation;
private @Mock SysuiStatusBarStateController mStatusBarStateController;
private @Mock KeyguardStateController mKeyguardStateController;
private @Mock NotificationShadeDepthController mNotificationShadeDepthController;
private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private @Mock ScreenOffAnimationController mScreenOffAnimationController;
- private @Mock FoldAodAnimationController mFoldAodAnimationController;
- private @Mock IKeyguardDrawnCallback mKeyguardDrawnCallback;
private @Mock InteractionJankMonitor mInteractionJankMonitor;
+ private @Mock ScreenOnCoordinator mScreenOnCoordinator;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
- private Optional<SysUIUnfoldComponent> mSysUiUnfoldComponentOptional;
-
private FalsingCollectorFake mFalsingCollector;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mFalsingCollector = new FalsingCollectorFake();
- mSysUiUnfoldComponentOptional = Optional.of(mSysUIUnfoldComponent);
when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
- when(mSysUIUnfoldComponent.getUnfoldLightRevealOverlayAnimation())
- .thenReturn(mUnfoldAnimation);
- when(mSysUIUnfoldComponent.getFoldAodAnimationController())
- .thenReturn(mFoldAodAnimationController);
-
when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
when(mInteractionJankMonitor.end(anyInt())).thenReturn(true);
@@ -151,33 +132,6 @@
}
@Test
- @TestableLooper.RunWithLooper(setAsMainLooper = true)
- public void testUnfoldTransitionEnabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback()
- throws RemoteException {
- mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
- TestableLooper.get(this).processAllMessages();
- onUnfoldOverlayReady();
- onFoldAodReady();
-
- // Should be called when both unfold overlay and keyguard drawn ready
- verify(mKeyguardDrawnCallback).onDrawn();
- }
-
- @Test
- @TestableLooper.RunWithLooper(setAsMainLooper = true)
- public void testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback()
- throws RemoteException {
- mSysUiUnfoldComponentOptional = Optional.empty();
- createAndStartViewMediator();
-
- mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
- TestableLooper.get(this).processAllMessages();
-
- // Should be called when only keyguard drawn
- verify(mKeyguardDrawnCallback).onDrawn();
- }
-
- @Test
public void testIsAnimatingScreenOff() {
when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
@@ -219,20 +173,6 @@
verify(mStatusBarKeyguardViewManager, atLeast(1)).show(null);
}
- private void onUnfoldOverlayReady() {
- ArgumentCaptor<Runnable> overlayReadyCaptor = ArgumentCaptor.forClass(Runnable.class);
- verify(mUnfoldAnimation).onScreenTurningOn(overlayReadyCaptor.capture());
- overlayReadyCaptor.getValue().run();
- TestableLooper.get(this).processAllMessages();
- }
-
- private void onFoldAodReady() {
- ArgumentCaptor<Runnable> ready = ArgumentCaptor.forClass(Runnable.class);
- verify(mFoldAodAnimationController).onScreenTurningOn(ready.capture());
- ready.getValue().run();
- TestableLooper.get(this).processAllMessages();
- }
-
private void createAndStartViewMediator() {
mViewMediator = new KeyguardViewMediator(
mContext,
@@ -251,12 +191,12 @@
mNavigationModeController,
mKeyguardDisplayManager,
mDozeParameters,
- mSysUiUnfoldComponentOptional,
mStatusBarStateController,
mKeyguardStateController,
() -> mKeyguardUnlockAnimationController,
mScreenOffAnimationController,
() -> mNotificationShadeDepthController,
+ mScreenOnCoordinator,
mInteractionJankMonitor);
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
index 2d1b258..53d0dd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
@@ -57,15 +57,16 @@
@Test
public void screenTurningOn() throws Exception {
- mScreen.dispatchScreenTurningOn();
+ Runnable onDrawn = () -> {};
+ mScreen.dispatchScreenTurningOn(onDrawn);
assertEquals(ScreenLifecycle.SCREEN_TURNING_ON, mScreen.getScreenState());
- verify(mScreenObserverMock).onScreenTurningOn();
+ verify(mScreenObserverMock).onScreenTurningOn(onDrawn);
}
@Test
public void screenTurnedOn() throws Exception {
- mScreen.dispatchScreenTurningOn();
+ mScreen.dispatchScreenTurningOn(null);
mScreen.dispatchScreenTurnedOn();
assertEquals(ScreenLifecycle.SCREEN_ON, mScreen.getScreenState());
@@ -74,7 +75,7 @@
@Test
public void screenTurningOff() throws Exception {
- mScreen.dispatchScreenTurningOn();
+ mScreen.dispatchScreenTurningOn(null);
mScreen.dispatchScreenTurnedOn();
mScreen.dispatchScreenTurningOff();
@@ -84,7 +85,7 @@
@Test
public void screenTurnedOff() throws Exception {
- mScreen.dispatchScreenTurningOn();
+ mScreen.dispatchScreenTurningOn(null);
mScreen.dispatchScreenTurnedOn();
mScreen.dispatchScreenTurningOff();
mScreen.dispatchScreenTurnedOff();
@@ -97,4 +98,4 @@
public void dump() throws Exception {
mScreen.dump(null, new PrintWriter(new ByteArrayOutputStream()), new String[0]);
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index b74ba26..927ca7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -19,14 +19,18 @@
import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
+import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.widget.ImageView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
@@ -50,24 +54,24 @@
@Test
fun displayChip_chipAdded() {
- controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
+ controllerCommon.displayChip(getState())
verify(windowManager).addView(any(), any())
}
@Test
fun displayChip_twice_chipNotAddedTwice() {
- controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
+ controllerCommon.displayChip(getState())
reset(windowManager)
- controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
+ controllerCommon.displayChip(getState())
verify(windowManager, never()).addView(any(), any())
}
@Test
fun removeChip_chipRemoved() {
// First, add the chip
- controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
+ controllerCommon.displayChip(getState())
// Then, remove it
controllerCommon.removeChip()
@@ -82,6 +86,29 @@
verify(windowManager, never()).removeView(any())
}
+ @Test
+ fun setIcon_viewHasIconAndContentDescription() {
+ controllerCommon.displayChip(getState())
+ val chipView = getChipView()
+ val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+ val contentDescription = "test description"
+
+ controllerCommon.setIcon(MediaTttChipState(drawable, contentDescription), chipView)
+
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(drawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(contentDescription)
+ }
+
+ private fun getState() = MediaTttChipState(appIconDrawable, APP_ICON_CONTENT_DESCRIPTION)
+
+ private fun getChipView(): ViewGroup {
+ val viewCaptor = ArgumentCaptor.forClass(View::class.java)
+ verify(windowManager).addView(viewCaptor.capture(), any())
+ return viewCaptor.value as ViewGroup
+ }
+
+ private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
+
inner class TestControllerCommon(
context: Context,
windowManager: WindowManager
@@ -92,3 +119,5 @@
}
}
}
+
+private const val APP_ICON_CONTENT_DESCRIPTION = "Content description"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 2ff472f..afaab80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media.taptotransfer.receiver
-import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.view.View
import android.view.ViewGroup
@@ -50,10 +49,12 @@
@Test
fun displayChip_chipContainsIcon() {
val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+ val contentDescription = "Test description"
- controllerReceiver.displayChip(ChipStateReceiver(drawable))
+ controllerReceiver.displayChip(ChipStateReceiver(drawable, contentDescription))
- assertThat(getChipView().getAppIconDrawable()).isEqualTo(drawable)
+ assertThat(getChipView().getAppIconView().drawable).isEqualTo(drawable)
+ assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(contentDescription)
}
private fun getChipView(): ViewGroup {
@@ -62,6 +63,5 @@
return viewCaptor.value as ViewGroup
}
- private fun ViewGroup.getAppIconDrawable(): Drawable =
- (this.requireViewById<ImageView>(R.id.app_icon)).drawable
+ private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 028ec55..caef5b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -70,7 +70,8 @@
controllerSender.displayChip(moveCloserToTransfer())
val chipView = getChipView()
- assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -85,7 +86,8 @@
// Assert we're still in the loading state
val chipView = getChipView()
- assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -155,7 +157,8 @@
controllerSender.displayChip(transferSucceeded())
val chipView = getChipView()
- assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
}
@@ -220,8 +223,7 @@
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
- private fun LinearLayout.getAppIconDrawable(): Drawable =
- (this.requireViewById<ImageView>(R.id.app_icon)).drawable
+ private fun LinearLayout.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
private fun LinearLayout.getChipText(): String =
(this.requireViewById<TextView>(R.id.text)).text as String
@@ -238,20 +240,22 @@
}
/** Helper method providing default parameters to not clutter up the tests. */
- private fun moveCloserToTransfer() = MoveCloserToTransfer(appIconDrawable, DEVICE_NAME)
+ private fun moveCloserToTransfer() =
+ MoveCloserToTransfer(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferInitiated(
future: Future<Runnable?> = TEST_FUTURE
- ) = TransferInitiated(appIconDrawable, DEVICE_NAME, future)
+ ) = TransferInitiated(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, future)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferSucceeded(
undoRunnable: Runnable? = null
- ) = TransferSucceeded(appIconDrawable, DEVICE_NAME, undoRunnable)
+ ) = TransferSucceeded(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoRunnable)
}
private const val DEVICE_NAME = "My Tablet"
+private const val APP_ICON_CONTENT_DESC = "Content description"
// Use a settable future that hasn't yet been set so that we don't immediately switch to the success
// state.
private val TEST_FUTURE: SettableFuture<Runnable?> = SettableFuture.create()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index af33daf..968b12a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.qs.tiles.CameraToggleTile
import com.android.systemui.qs.tiles.CastTile
import com.android.systemui.qs.tiles.CellularTile
+import com.android.systemui.qs.tiles.ColorCorrectionTile
import com.android.systemui.qs.tiles.ColorInversionTile
import com.android.systemui.qs.tiles.DataSaverTile
import com.android.systemui.qs.tiles.DeviceControlsTile
@@ -91,7 +92,8 @@
"wallet" to QuickAccessWalletTile::class.java,
"qr_code_scanner" to QRCodeScannerTile::class.java,
"onehanded" to OneHandedModeTile::class.java,
- "fgsmanager" to FgsManagerTile::class.java
+ "fgsmanager" to FgsManagerTile::class.java,
+ "color_correction" to ColorCorrectionTile::class.java
)
@RunWith(AndroidTestingRunner::class)
@@ -132,6 +134,7 @@
@Mock private lateinit var qrCodeScannerTile: QRCodeScannerTile
@Mock private lateinit var oneHandedModeTile: OneHandedModeTile
@Mock private lateinit var fgsManagerTile: FgsManagerTile
+ @Mock private lateinit var colorCorrectionTile: ColorCorrectionTile
private lateinit var factory: QSFactoryImpl
@@ -175,7 +178,8 @@
{ quickAccessWalletTile },
{ qrCodeScannerTile },
{ oneHandedModeTile },
- { fgsManagerTile }
+ { fgsManagerTile },
+ { colorCorrectionTile }
)
// When adding/removing tiles, fix also [specMap]
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
new file mode 100644
index 0000000..debe41c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.SecureSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class ColorCorrectionTileTest extends SysuiTestCase {
+
+ @Mock
+ private QSTileHost mHost;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private QSLogger mQSLogger;
+ @Mock
+ private UiEventLogger mUiEventLogger;
+ @Mock
+ private UserTracker mUserTracker;
+
+ private TestableLooper mTestableLooper;
+ private SecureSettings mSecureSettings;
+ private ColorCorrectionTile mTile;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mSecureSettings = new FakeSettings();
+ mTestableLooper = TestableLooper.get(this);
+
+ when(mHost.getContext()).thenReturn(mContext);
+ when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
+
+ mTile = new ColorCorrectionTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mUserTracker,
+ mSecureSettings
+ );
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
+ public void longClick_expectedAction() {
+ final ArgumentCaptor<Intent> IntentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ mTile.longClick(/* view= */ null);
+ mTestableLooper.processAllMessages();
+
+ verify(mActivityStarter).postStartActivityDismissingKeyguard(IntentCaptor.capture(),
+ anyInt(), any());
+ assertThat(IntentCaptor.getValue().getAction()).isEqualTo(
+ Settings.ACTION_COLOR_CORRECTION_SETTINGS);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 9bf8775..8a38847 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -86,6 +87,7 @@
super(mock(HeadsUpManagerLogger.class));
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
+ mHandler.removeCallbacksAndMessages(null);
mHandler = mTestHandler;
}
@@ -145,6 +147,11 @@
mAlertingNotificationManager = createAlertingNotificationManager();
}
+ @After
+ public void tearDown() {
+ mTestHandler.removeCallbacksAndMessages(null);
+ }
+
@Test
public void testShowNotification_addsEntry() {
mAlertingNotificationManager.showNotification(mEntry);
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 e427d53..7f35732 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -81,6 +81,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardIndication;
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
+import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -154,6 +155,8 @@
private IActivityManager mIActivityManager;
@Mock
private KeyguardBypassController mKeyguardBypassController;
+ @Mock
+ private ScreenLifecycle mScreenLifecycle;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
@Captor
@@ -195,7 +198,7 @@
R.string.do_financed_disclosure_with_name, ORGANIZATION_NAME);
when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
- when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
+ when(mScreenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_ON);
when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true);
when(mIndicationArea.findViewById(R.id.keyguard_indication_text_bottom))
@@ -225,8 +228,8 @@
mController = new KeyguardIndicationController(mContext, mWakeLockBuilder,
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
- mUserManager, mExecutor, mFalsingManager, mLockPatternUtils, mIActivityManager,
- mKeyguardBypassController);
+ mUserManager, mExecutor, mFalsingManager, mLockPatternUtils, mScreenLifecycle,
+ mIActivityManager, mKeyguardBypassController);
mController.init();
mController.setIndicationArea(mIndicationArea);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
@@ -702,7 +705,6 @@
// GIVEN state of showing message when keyguard screen is on
when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
// GIVEN fingerprint is also running (not udfps)
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index c74437f..3a60c04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
@@ -4,6 +4,7 @@
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
@@ -29,6 +30,7 @@
@Mock lateinit var notificationShadeWindowViewController: NotificationShadeWindowViewController
@Mock lateinit var notificationListContainer: NotificationListContainer
@Mock lateinit var headsUpManager: HeadsUpManagerPhone
+ @Mock lateinit var jankMonitor: InteractionJankMonitor
private lateinit var notificationTestHelper: NotificationTestHelper
private lateinit var notification: ExpandableNotificationRow
@@ -49,7 +51,8 @@
notificationShadeWindowViewController,
notificationListContainer,
headsUpManager,
- notification
+ notification,
+ jankMonitor
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
index a3569e4..a11b46d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
@@ -19,8 +19,11 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -44,6 +47,8 @@
import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -52,6 +57,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -75,6 +82,9 @@
@Mock private NodeController mHeaderController;
private NotificationEntry mEntry;
+ private final FakeSystemClock mClock = new FakeSystemClock();
+ private final FakeExecutor mExecutor = new FakeExecutor(mClock);
+ private final ArrayList<NotificationEntry> mHuns = new ArrayList();
@Before
public void setUp() {
@@ -85,7 +95,8 @@
mHeadsUpViewBinder,
mNotificationInterruptStateProvider,
mRemoteInputManager,
- mHeaderController);
+ mHeaderController,
+ mExecutor);
mCoordinator.attach(mNotifPipeline);
@@ -105,6 +116,16 @@
notifLifetimeExtenderCaptor.capture());
verify(mHeadsUpManager).addListener(headsUpChangedListenerCaptor.capture());
+ given(mHeadsUpManager.getAllEntries()).willAnswer(i -> mHuns.stream());
+ given(mHeadsUpManager.isAlerting(anyString())).willAnswer(i -> {
+ String key = i.getArgument(0);
+ for (NotificationEntry entry : mHuns) {
+ if (entry.getKey().equals(key)) return true;
+ }
+ return false;
+ });
+ when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L);
+
mCollectionListener = notifCollectionCaptor.getValue();
mNotifPromoter = notifPromoterCaptor.getValue();
mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue();
@@ -116,63 +137,64 @@
}
@Test
+ public void testCancelStickyNotification() {
+ when(mHeadsUpManager.isSticky(anyString())).thenReturn(true);
+ addHUN(mEntry);
+ when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 0L);
+ assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, 0));
+ mClock.advanceTime(1000L);
+ mExecutor.runAllReady();
+ verify(mHeadsUpManager, times(1))
+ .removeNotification(anyString(), eq(true));
+ }
+
+ @Test
+ public void testCancelUpdatedStickyNotification() {
+ when(mHeadsUpManager.isSticky(anyString())).thenReturn(true);
+ addHUN(mEntry);
+ when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L);
+ assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, 0));
+ mClock.advanceTime(1000L);
+ mExecutor.runAllReady();
+ verify(mHeadsUpManager, times(0))
+ .removeNotification(anyString(), eq(true));
+ }
+
+ @Test
public void testPromotesCurrentHUN() {
// GIVEN the current HUN is set to mEntry
- setCurrentHUN(mEntry);
+ addHUN(mEntry);
// THEN only promote the current HUN, mEntry
assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry));
- assertFalse(mNotifPromoter.shouldPromoteToTopLevel(new NotificationEntryBuilder().build()));
+ assertFalse(mNotifPromoter.shouldPromoteToTopLevel(new NotificationEntryBuilder()
+ .setPkg("test-package2")
+ .build()));
}
@Test
public void testIncludeInSectionCurrentHUN() {
// GIVEN the current HUN is set to mEntry
- setCurrentHUN(mEntry);
+ addHUN(mEntry);
// THEN only section the current HUN, mEntry
assertTrue(mNotifSectioner.isInSection(mEntry));
- assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder().build()));
+ assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder()
+ .setPkg("test-package")
+ .build()));
}
@Test
public void testLifetimeExtendsCurrentHUN() {
// GIVEN there is a HUN, mEntry
- setCurrentHUN(mEntry);
+ addHUN(mEntry);
// THEN only the current HUN, mEntry, should be lifetimeExtended
assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, /* cancellationReason */ 0));
assertFalse(mNotifLifetimeExtender.shouldExtendLifetime(
- new NotificationEntryBuilder().build(), /* cancellationReason */ 0));
- }
-
- @Test
- public void testLifetimeExtensionEndsOnNewHUN() {
- // GIVEN there was a HUN that was lifetime extended
- setCurrentHUN(mEntry);
- assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
- mEntry, /* cancellation reason */ 0));
-
- // WHEN there's a new HUN
- NotificationEntry newHUN = new NotificationEntryBuilder().build();
- setCurrentHUN(newHUN);
-
- // THEN the old entry's lifetime extension should be cancelled
- verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
- }
-
- @Test
- public void testLifetimeExtensionEndsOnNoHUNs() {
- // GIVEN there was a HUN that was lifetime extended
- setCurrentHUN(mEntry);
- assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
- mEntry, /* cancellation reason */ 0));
-
- // WHEN there's no longer a HUN
- setCurrentHUN(null);
-
- // THEN the old entry's lifetime extension should be cancelled
- verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
+ new NotificationEntryBuilder()
+ .setPkg("test-package")
+ .build(), /* cancellationReason */ 0));
}
@Test
@@ -208,7 +230,7 @@
@Test
public void testOnEntryRemovedRemovesHeadsUpNotification() {
// GIVEN the current HUN is mEntry
- setCurrentHUN(mEntry);
+ addHUN(mEntry);
// WHEN mEntry is removed from the notification collection
mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0);
@@ -218,12 +240,9 @@
verify(mHeadsUpManager).removeNotification(mEntry.getKey(), false);
}
- private void setCurrentHUN(NotificationEntry entry) {
+ private void addHUN(NotificationEntry entry) {
+ mHuns.add(entry);
when(mHeadsUpManager.getTopEntry()).thenReturn(entry);
- when(mHeadsUpManager.isAlerting(any())).thenReturn(false);
- if (entry != null) {
- when(mHeadsUpManager.isAlerting(entry.getKey())).thenReturn(true);
- }
mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, entry != null);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
index 2dfb9fc..429d2ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
@@ -60,6 +60,7 @@
import com.google.android.collect.Lists;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -131,6 +132,11 @@
verify(mNotifPipeline, never()).addCollectionListener(any());
}
+ @After
+ public void tearDown() {
+ mLogger.mHandler.removeCallbacksAndMessages(null);
+ }
+
@Test
public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
NotificationVisibility[] newlyVisibleKeys = {
@@ -281,6 +287,7 @@
mNotificationPanelLoggerFake
);
mBarService = barService;
+ mHandler.removeCallbacksAndMessages(null);
// Make this on the current thread so we can wait for it during tests.
mHandler = Handler.createAsync(Looper.myLooper());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 4d861f9..b69bd8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -60,6 +60,7 @@
import com.google.android.collect.Lists;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -132,6 +133,11 @@
verify(mNotifPipeline).addCollectionListener(any());
}
+ @After
+ public void tearDown() {
+ mLogger.mHandler.removeCallbacksAndMessages(null);
+ }
+
@Test
public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
NotificationVisibility[] newlyVisibleKeys = {
@@ -282,6 +288,7 @@
mNotificationPanelLoggerFake
);
mBarService = barService;
+ mHandler.removeCallbacksAndMessages(null);
// Make this on the current thread so we can wait for it during tests.
mHandler = Handler.createAsync(Looper.myLooper());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index f3eece8..e4721b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -36,6 +36,7 @@
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.drawable.Icon;
+import android.os.Handler;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.testing.TestableLooper;
@@ -139,6 +140,8 @@
mock(NotificationGroupManagerLegacy.class),
mock(ConfigurationControllerImpl.class)
);
+ mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
+ mHeadsUpManager.mHandler = new Handler(mTestLooper.getLooper());
mGroupMembershipManager.setHeadsUpManager(mHeadsUpManager);
mIconManager = new IconManager(
mock(CommonNotifCollection.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 276f246..4d2c0c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -32,6 +32,7 @@
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -46,9 +47,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
@@ -56,7 +55,6 @@
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
-import com.android.systemui.statusbar.notification.people.PeopleHubViewAdapter;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
@@ -66,6 +64,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -266,8 +265,9 @@
// THEN the header is first removed from the transient parent before being added to the
// NSSL.
- verify(transientParent).removeTransientView(silentHeaderView);
- verify(mNssl).addView(silentHeaderView, 1);
+ final InOrder inOrder = inOrder(silentHeaderView, mNssl);
+ inOrder.verify(silentHeaderView).removeFromTransientContainer();
+ inOrder.verify(mNssl).addView(eq(silentHeaderView), eq(1));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 7194c66..4cc1be6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -38,6 +38,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -135,6 +136,7 @@
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private ShadeController mShadeController;
+ @Mock private InteractionJankMonitor mJankMonitor;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -188,7 +190,8 @@
mLayoutInflater,
mRemoteInputManager,
mVisualStabilityManager,
- mShadeController
+ mShadeController,
+ mJankMonitor
);
when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
new file mode 100644
index 0000000..649dc23
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.FoldStateListener.OnFoldStateChangeListener
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FoldStateListenerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var listener: OnFoldStateChangeListener
+ private lateinit var sut: FoldStateListener
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ setFoldedStates(DEVICE_STATE_FOLDED)
+ setGoToSleepStates(DEVICE_STATE_FOLDED)
+ sut = FoldStateListener(mContext, listener)
+ }
+
+ @Test
+ fun onStateChanged_stateFolded_notifiesWithFoldedAndGoingToSleep() {
+ sut.onStateChanged(DEVICE_STATE_FOLDED)
+
+ verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateHalfFolded_notifiesWithNotFoldedAndNotGoingToSleep() {
+ sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+
+ verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateUnfolded_notifiesWithNotFoldedAndNotGoingToSleep() {
+ sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+
+ verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateUnfoldedThenHalfFolded_notifiesOnce() {
+ sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+ sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+
+ verify(listener, times(1)).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateHalfFoldedThenUnfolded_notifiesOnce() {
+ sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+ sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+
+ verify(listener, times(1)).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateHalfFoldedThenFolded_notifiesTwice() {
+ sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+ sut.onStateChanged(DEVICE_STATE_FOLDED)
+
+ val inOrder = Mockito.inOrder(listener)
+ inOrder.verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ inOrder.verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateFoldedThenHalfFolded_notifiesTwice() {
+ sut.onStateChanged(DEVICE_STATE_FOLDED)
+ sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+
+ val inOrder = Mockito.inOrder(listener)
+ inOrder.verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
+ inOrder.verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ }
+
+ private fun setGoToSleepStates(vararg states: Int) {
+ mContext.orCreateTestableResources.addOverride(
+ R.array.config_deviceStatesOnWhichToSleep,
+ states
+ )
+ }
+
+ private fun setFoldedStates(vararg states: Int) {
+ mContext.orCreateTestableResources.addOverride(
+ R.array.config_foldedDeviceStates,
+ states
+ )
+ }
+
+ companion object {
+ private const val DEVICE_STATE_FOLDED = 123
+ private const val DEVICE_STATE_HALF_FOLDED = 456
+ private const val DEVICE_STATE_UNFOLDED = 789
+
+ private const val FOLDED = true
+ private const val NOT_FOLDED = false
+
+ private const val WILL_GO_TO_SLEEP = true
+ private const val WILL_NOT_SLEEP = false
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 0f419c7..e8b9c7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -41,6 +41,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -115,9 +116,15 @@
mConfigurationController
);
super.setUp();
+ mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
mHeadsUpManager.mHandler = mTestHandler;
}
+ @After
+ public void tearDown() {
+ mTestHandler.removeCallbacksAndMessages(null);
+ }
+
@Test
public void testSnooze() {
mHeadsUpManager.showNotification(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index e5f2aa7..f391eff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -43,7 +44,6 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardHostViewController;
-import com.android.keyguard.KeyguardRootViewController;
import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
@@ -64,7 +64,6 @@
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import org.mockito.stubbing.Answer;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -92,13 +91,10 @@
@Mock
private KeyguardSecurityModel mKeyguardSecurityModel;
@Mock
- private KeyguardRootViewController mRootViewController;
- @Mock
- private ViewGroup mRootView;
- @Mock
private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
@Mock
private KeyguardBouncerComponent mKeyguardBouncerComponent;
+ private ViewGroup mContainer;
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
private Integer mRootVisibility = View.INVISIBLE;
@@ -107,32 +103,22 @@
@Before
public void setup() {
allowTestableLooperAsMainThread();
- mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
- mDependency.injectMockDependency(KeyguardStateController.class);
- when(mRootView.getVisibility()).thenAnswer((Answer<Integer>) invocation -> mRootVisibility);
- doAnswer(invocation -> {
- mRootVisibility = invocation.getArgument(0);
- return null;
- }).when(mRootView).setVisibility(anyInt());
when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
.thenReturn(KeyguardSecurityModel.SecurityMode.None);
DejankUtils.setImmediate(true);
- when(mKeyguardBouncerComponentFactory.create()).thenReturn(mKeyguardBouncerComponent);
+
+ mContainer = spy(new FrameLayout(getContext()));
+ when(mKeyguardBouncerComponentFactory.create(mContainer)).thenReturn(
+ mKeyguardBouncerComponent);
when(mKeyguardBouncerComponent.getKeyguardHostViewController())
.thenReturn(mKeyguardHostViewController);
- when(mKeyguardBouncerComponent.getKeyguardRootViewController())
- .thenReturn(mRootViewController);
- when(mRootViewController.getView()).thenReturn(mRootView);
- when(mRootView.getResources()).thenReturn(mContext.getResources());
-
- final ViewGroup container = new FrameLayout(getContext());
mBouncer = new KeyguardBouncer.Factory(getContext(), mViewMediatorCallback,
mDismissCallbackRegistry, mFalsingCollector,
mKeyguardStateController, mKeyguardUpdateMonitor,
mKeyguardBypassController, mHandler, mKeyguardSecurityModel,
mKeyguardBouncerComponentFactory)
- .create(container, mExpansionCallback);
+ .create(mContainer, mExpansionCallback);
}
@Test
@@ -233,7 +219,7 @@
mBouncer.setExpansion(0);
verify(mKeyguardHostViewController).onResume();
- verify(mRootView).announceForAccessibility(any());
+ verify(mContainer).announceForAccessibility(any());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 1182695..1827c7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -62,6 +62,7 @@
private float mPanelExpansion;
private int mKeyguardStatusBarHeaderHeight;
private int mKeyguardStatusHeight;
+ private int mUserSwitchHeight;
private float mDark;
private float mQsExpansion;
private int mCutoutTopInset = 0;
@@ -264,8 +265,7 @@
@Test
public void clockPositionedDependingOnMarginInSplitShade() {
- when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
- .thenReturn(400);
+ setSplitShadeTopMargin(400);
mClockPositionAlgorithm.loadDimens(mResources);
givenLockScreen();
mIsSplitShade = true;
@@ -291,6 +291,32 @@
}
@Test
+ public void notifPaddingAccountsForMultiUserSwitcherInSplitShade() {
+ setSplitShadeTopMargin(100);
+ mUserSwitchHeight = 150;
+ mClockPositionAlgorithm.loadDimens(mResources);
+ givenLockScreen();
+ mIsSplitShade = true;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is split shade top margin + user switch height
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(250);
+ }
+
+ @Test
+ public void clockDoesntAccountForMultiUserSwitcherInSplitShade() {
+ setSplitShadeTopMargin(100);
+ mUserSwitchHeight = 150;
+ mClockPositionAlgorithm.loadDimens(mResources);
+ givenLockScreen();
+ mIsSplitShade = true;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN clockY = split shade top margin
+ assertThat(mClockPosition.clockY).isEqualTo(100);
+ }
+
+ @Test
public void notifPaddingExpandedAlignedWithClockInSplitShadeMode() {
givenLockScreen();
mIsSplitShade = true;
@@ -495,6 +521,11 @@
assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset);
}
+ private void setSplitShadeTopMargin(int value) {
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
+ .thenReturn(value);
+ }
+
private void givenHighestBurnInOffset() {
when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).then(returnsFirstArg());
}
@@ -529,7 +560,7 @@
mKeyguardStatusBarHeaderHeight,
mPanelExpansion,
mKeyguardStatusHeight,
- 0 /* userSwitchHeight */,
+ mUserSwitchHeight,
0 /* userSwitchPreferredY */,
mDark,
ZERO_DRAG,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index b717d28..9898b4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.wm.shell.bubbles.Bubbles;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -111,6 +112,11 @@
mHeadsUpManager.addListener(mGroupAlertTransferHelper);
}
+ @After
+ public void tearDown() {
+ mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
+ }
+
private void mockHasHeadsUpContentView(NotificationEntry entry,
boolean hasHeadsUpContentView) {
RowContentBindParams params = new RowContentBindParams();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 1cd9b9e..5ada6d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -141,6 +141,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
@@ -148,6 +149,7 @@
import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.android.wm.shell.animation.FlingAnimationUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -358,6 +360,8 @@
private NotificationsQSContainerController mNotificationsQSContainerController;
@Mock
private QsFrameTranslateController mQsFrameTranslateController;
+ @Mock
+ private StatusBarWindowStateController mStatusBarWindowStateController;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -366,6 +370,7 @@
private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners;
private FalsingManagerFake mFalsingManager = new FalsingManagerFake();
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private Handler mMainHandler;
@Before
public void setup() {
@@ -483,15 +488,19 @@
.thenReturn(true);
reset(mView);
+ mMainHandler = new Handler(Looper.getMainLooper());
+
mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources,
- new Handler(Looper.getMainLooper()),
+ mMainHandler,
mLayoutInflater,
mFeatureFlags,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
mFalsingManager, new FalsingCollectorFake(),
mNotificationLockscreenUserManager, mNotificationEntryManager,
- mCommunalStateController, mKeyguardStateController, mStatusBarStateController,
+ mCommunalStateController, mKeyguardStateController,
+ mStatusBarStateController,
+ mStatusBarWindowStateController,
mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
mCommunalSourceMonitor, mMetricsLogger, mActivityManager, mConfigurationController,
@@ -560,6 +569,12 @@
.setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class));
}
+ @After
+ public void tearDown() {
+ mNotificationPanelViewController.cancelHeightAnimator();
+ mMainHandler.removeCallbacksAndMessages(null);
+ }
+
@Test
public void testSetPanelScrimMinFraction() {
mNotificationPanelViewController.setPanelScrimMinFraction(0.5f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
new file mode 100644
index 0000000..12e71af
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.MotionEvent
+import androidx.test.filters.SmallTest
+import com.android.keyguard.LockIconViewController
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.dock.DockManager
+import com.android.systemui.statusbar.LockscreenShadeTransitionController
+import com.android.systemui.statusbar.NotificationShadeDepthController
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.NotificationShadeWindowView.InteractionEventHandler
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
+import com.android.systemui.statusbar.window.StatusBarWindowStateController
+import com.android.systemui.tuner.TunerService
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
+ private lateinit var mController: NotificationShadeWindowViewController
+
+ @Mock
+ private lateinit var mView: NotificationShadeWindowView
+ @Mock
+ private lateinit var mTunerService: TunerService
+ @Mock
+ private lateinit var mStatusBarStateController: SysuiStatusBarStateController
+ @Mock
+ private lateinit var mStatusBar: StatusBar
+ @Mock
+ private lateinit var mDockManager: DockManager
+ @Mock
+ private lateinit var mNotificationPanelViewController: NotificationPanelViewController
+ @Mock
+ private lateinit var mNotificationShadeDepthController: NotificationShadeDepthController
+ @Mock
+ private lateinit var mNotificationShadeWindowController: NotificationShadeWindowController
+ @Mock
+ private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
+ @Mock
+ private lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock
+ private lateinit var mStatusBarWindowStateController: StatusBarWindowStateController
+ @Mock
+ private lateinit var mLockscreenShadeTransitionController: LockscreenShadeTransitionController
+ @Mock
+ private lateinit var mLockIconViewController: LockIconViewController
+ @Mock
+ private lateinit var mPhoneStatusBarViewController: PhoneStatusBarViewController
+
+ private lateinit var mInteractionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
+ private lateinit var mInteractionEventHandler: InteractionEventHandler
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(mView.bottom).thenReturn(VIEW_BOTTOM)
+
+ mController = NotificationShadeWindowViewController(
+ mLockscreenShadeTransitionController,
+ FalsingCollectorFake(),
+ mTunerService,
+ mStatusBarStateController,
+ mDockManager,
+ mNotificationShadeDepthController,
+ mView,
+ mNotificationPanelViewController,
+ PanelExpansionStateManager(),
+ stackScrollLayoutController,
+ mStatusBarKeyguardViewManager,
+ mStatusBarWindowStateController,
+ mLockIconViewController
+ )
+ mController.setupExpandedStatusBar()
+ mController.setService(mStatusBar, mNotificationShadeWindowController)
+
+ mInteractionEventHandlerCaptor =
+ ArgumentCaptor.forClass(InteractionEventHandler::class.java)
+ verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture())
+ mInteractionEventHandler = mInteractionEventHandlerCaptor.value
+ }
+
+ // Note: So far, these tests only cover interactions with the status bar view controller. More
+ // tests need to be added to test the rest of handleDispatchTouchEvent.
+
+ @Test
+ fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() {
+ mController.setStatusBarViewController(null)
+
+ val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
+
+ assertThat(returnVal).isFalse()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() {
+ mController.setStatusBarViewController(mPhoneStatusBarViewController)
+ val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
+ whenever(mPhoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
+
+ val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(ev)
+
+ verify(mPhoneStatusBarViewController).sendTouchToView(ev)
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
+ mController.setStatusBarViewController(mPhoneStatusBarViewController)
+ val downEvBelow = MotionEvent.obtain(
+ 0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0
+ )
+ mInteractionEventHandler.handleDispatchTouchEvent(downEvBelow)
+
+ val nextEvent = MotionEvent.obtain(
+ 0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0
+ )
+ whenever(mPhoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
+
+ val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(nextEvent)
+
+ verify(mPhoneStatusBarViewController).sendTouchToView(nextEvent)
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() {
+ mController.setStatusBarViewController(mPhoneStatusBarViewController)
+ whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+ whenever(mPhoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true)
+
+ val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
+
+ verify(mPhoneStatusBarViewController).sendTouchToView(downEv)
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() {
+ mController.setStatusBarViewController(mPhoneStatusBarViewController)
+ whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+ // Item we're testing
+ whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(false)
+
+ val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
+
+ verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv)
+ assertThat(returnVal).isNull()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() {
+ mController.setStatusBarViewController(mPhoneStatusBarViewController)
+ whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ // Item we're testing
+ whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(false)
+
+ val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
+
+ verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv)
+ assertThat(returnVal).isNull()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() {
+ mController.setStatusBarViewController(mPhoneStatusBarViewController)
+ whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+ // Item we're testing
+ whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(false)
+
+ val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
+
+ verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv)
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() {
+ mController.setStatusBarViewController(mPhoneStatusBarViewController)
+ whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+
+ // Down event first
+ mInteractionEventHandler.handleDispatchTouchEvent(downEv)
+
+ // Then another event
+ val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
+ whenever(mPhoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
+
+ val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(nextEvent)
+
+ verify(mPhoneStatusBarViewController).sendTouchToView(nextEvent)
+ assertThat(returnVal).isTrue()
+ }
+}
+
+private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+private const val VIEW_BOTTOM = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 1adba6e..d885da8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -37,24 +37,15 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.dock.DockManager;
-import com.android.systemui.doze.DozeLog;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.tuner.TunerService;
import org.junit.Before;
@@ -73,29 +64,19 @@
private NotificationShadeWindowView mView;
private NotificationShadeWindowViewController mController;
- @Mock private NotificationWakeUpCoordinator mCoordinator;
- @Mock private PulseExpansionHandler mPulseExpansionHandler;
- @Mock private DynamicPrivacyController mDynamicPrivacyController;
- @Mock private KeyguardBypassController mBypassController;
- @Mock private PluginManager mPluginManager;
@Mock private TunerService mTunerService;
@Mock private DragDownHelper mDragDownHelper;
- @Mock private KeyguardStateController mKeyguardStateController;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private ShadeController mShadeController;
- @Mock private NotificationLockscreenUserManager mNotificationLockScreenUserManager;
- @Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private StatusBar mStatusBar;
- @Mock private DozeLog mDozeLog;
- @Mock private DozeParameters mDozeParameters;
@Mock private DockManager mDockManager;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock private NotificationShadeDepthController mNotificationShadeDepthController;
- @Mock private StatusBarWindowController mStatusBarWindowController;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock private LockIconViewController mLockIconViewController;
@@ -117,30 +98,18 @@
when(mDockManager.isDocked()).thenReturn(false);
mController = new NotificationShadeWindowViewController(
- mCoordinator,
- mPulseExpansionHandler,
- mDynamicPrivacyController,
- mBypassController,
mLockscreenShadeTransitionController,
new FalsingCollectorFake(),
- mPluginManager,
mTunerService,
- mNotificationLockScreenUserManager,
- mNotificationEntryManager,
- mKeyguardStateController,
mStatusBarStateController,
- mDozeLog,
- mDozeParameters,
- new CommandQueue(mContext),
- mShadeController,
mDockManager,
mNotificationShadeDepthController,
mView,
mNotificationPanelViewController,
new PanelExpansionStateManager(),
- mStatusBarWindowController,
mNotificationStackScrollLayoutController,
mStatusBarKeyguardViewManager,
+ mStatusBarWindowStateController,
mLockIconViewController);
mController.setupExpandedStatusBar();
mController.setService(mStatusBar, mNotificationShadeWindowController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 235de1e..c65a6b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -77,9 +77,10 @@
val parent = FrameLayout(mContext) // add parent to keep layout params
view = LayoutInflater.from(mContext)
.inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
+ view.setLeftTopRightBottom(VIEW_LEFT, VIEW_TOP, VIEW_RIGHT, VIEW_BOTTOM)
}
- controller = createController(view)
+ controller = createAndInitController(view)
}
@Test
@@ -99,8 +100,7 @@
val view = createViewMock()
val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
unfoldConfig.isEnabled = true
- controller = createController(view)
- controller.init()
+ controller = createAndInitController(view)
verify(view.viewTreeObserver).addOnPreDrawListener(argumentCaptor.capture())
argumentCaptor.value.onPreDraw()
@@ -108,6 +108,64 @@
verify(moveFromCenterAnimation).onViewsReady(any())
}
+ @Test
+ fun touchIsWithinView_inBounds_returnsTrue() {
+ val view = createViewMockWithScreenLocation()
+ controller = createAndInitController(view)
+
+ assertThat(controller.touchIsWithinView(VIEW_LEFT + 1f, VIEW_TOP + 1f)).isTrue()
+ }
+
+ @Test
+ fun touchIsWithinView_onTopLeftCorner_returnsTrue() {
+ val view = createViewMockWithScreenLocation()
+ controller = createAndInitController(view)
+
+ assertThat(controller.touchIsWithinView(VIEW_LEFT.toFloat(), VIEW_TOP.toFloat())).isTrue()
+ }
+
+ @Test
+ fun touchIsWithinView_onBottomRightCorner_returnsTrue() {
+ val view = createViewMockWithScreenLocation()
+ controller = createAndInitController(view)
+
+ assertThat(controller.touchIsWithinView(
+ VIEW_RIGHT.toFloat(), VIEW_BOTTOM.toFloat())
+ ).isTrue()
+ }
+
+ @Test
+ fun touchIsWithinView_xTooSmall_returnsFalse() {
+ val view = createViewMockWithScreenLocation()
+ controller = createAndInitController(view)
+
+ assertThat(controller.touchIsWithinView(VIEW_LEFT - 1f, VIEW_TOP + 1f)).isFalse()
+ }
+
+ @Test
+ fun touchIsWithinView_xTooLarge_returnsFalse() {
+ val view = createViewMockWithScreenLocation()
+ controller = createAndInitController(view)
+
+ assertThat(controller.touchIsWithinView(VIEW_RIGHT + 1f, VIEW_TOP + 1f)).isFalse()
+ }
+
+ @Test
+ fun touchIsWithinView_yTooSmall_returnsFalse() {
+ val view = createViewMockWithScreenLocation()
+ controller = createAndInitController(view)
+
+ assertThat(controller.touchIsWithinView(VIEW_LEFT + 1f, VIEW_TOP - 1f)).isFalse()
+ }
+
+ @Test
+ fun touchIsWithinView_yTooLarge_returnsFalse() {
+ val view = createViewMockWithScreenLocation()
+ controller = createAndInitController(view)
+
+ assertThat(controller.touchIsWithinView(VIEW_LEFT + 1f, VIEW_BOTTOM + 1f)).isFalse()
+ }
+
private fun createViewMock(): PhoneStatusBarView {
val view = spy(view)
val viewTreeObserver = mock(ViewTreeObserver::class.java)
@@ -116,12 +174,23 @@
return view
}
- private fun createController(view: PhoneStatusBarView): PhoneStatusBarViewController {
+ private fun createViewMockWithScreenLocation(): PhoneStatusBarView {
+ val view = spy(view)
+ val location = IntArray(2)
+ location[0] = VIEW_LEFT
+ location[1] = VIEW_TOP
+ `when`(view.locationOnScreen).thenReturn(location)
+ return view
+ }
+
+ private fun createAndInitController(view: PhoneStatusBarView): PhoneStatusBarViewController {
return PhoneStatusBarViewController.Factory(
Optional.of(sysuiUnfoldComponent),
Optional.of(progressProvider),
configurationController
- ).create(view, touchEventHandler)
+ ).create(view, touchEventHandler).also {
+ it.init()
+ }
}
private class UnfoldConfig : UnfoldTransitionConfig {
@@ -142,3 +211,8 @@
}
}
}
+
+private const val VIEW_LEFT = 30
+private const val VIEW_RIGHT = 100
+private const val VIEW_TOP = 40
+private const val VIEW_BOTTOM = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
index 0131293..aabf923 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
@@ -36,7 +36,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
@@ -46,8 +45,6 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import org.junit.Before;
@@ -84,6 +81,7 @@
@Mock private VibratorHelper mVibratorHelper;
@Mock private Vibrator mVibrator;
@Mock private LightBarController mLightBarController;
+ @Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
StatusBarCommandQueueCallbacks mSbcqCallbacks;
@@ -112,8 +110,7 @@
mStatusBarStateController,
mNotificationShadeWindowView,
mNotificationStackScrollLayoutController,
- new StatusBarHideIconsForBouncerManager(
- mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
+ mStatusBarHideIconsForBouncerManager,
mPowerManager,
mVibratorHelper,
Optional.of(mVibrator),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 07ec0e2..743311f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -48,6 +48,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
@@ -64,7 +65,6 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -143,6 +143,8 @@
private StatusBarNotificationActivityStarter mNotificationActivityStarter;
@Mock
private ActivityLaunchAnimator mActivityLaunchAnimator;
+ @Mock
+ private InteractionJankMonitor mJankMonitor;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private NotificationTestHelper mNotificationTestHelper;
@@ -197,7 +199,8 @@
new NotificationLaunchAnimatorControllerProvider(
mock(NotificationShadeWindowViewController.class), mock(
NotificationListContainer.class),
- headsUpManager);
+ headsUpManager,
+ mJankMonitor);
mNotificationActivityStarter =
new StatusBarNotificationActivityStarter.Builder(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 9d5b17e..bb8bad3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -44,6 +44,7 @@
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.IntentFilter;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
@@ -69,6 +70,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
@@ -147,6 +149,7 @@
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.MessageRouterImpl;
@@ -160,6 +163,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -174,6 +178,9 @@
@RunWithLooper(setAsMainLooper = true)
public class StatusBarTest extends SysuiTestCase {
+ private static final int FOLD_STATE_FOLDED = 0;
+ private static final int FOLD_STATE_UNFOLDED = 1;
+
private StatusBar mStatusBar;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
@@ -228,6 +235,7 @@
@Mock private DynamicPrivacyController mDynamicPrivacyController;
@Mock private AutoHideController mAutoHideController;
@Mock private StatusBarWindowController mStatusBarWindowController;
+ @Mock private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager;
@Mock private UserSwitcherController mUserSwitcherController;
@Mock private NetworkController mNetworkController;
@@ -263,6 +271,7 @@
@Mock private BrightnessSliderController.Factory mBrightnessSliderFactory;
@Mock private WallpaperController mWallpaperController;
@Mock private OngoingCallController mOngoingCallController;
+ @Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock private LockscreenShadeTransitionController mLockscreenTransitionController;
@Mock private FeatureFlags mFeatureFlags;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@@ -276,6 +285,8 @@
@Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock private NotifPipelineFlags mNotifPipelineFlags;
@Mock private NotifLiveDataStore mNotifLiveDataStore;
+ @Mock private InteractionJankMonitor mJankMonitor;
+ @Mock private DeviceStateManager mDeviceStateManager;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -374,6 +385,7 @@
mLightBarController,
mAutoHideController,
mStatusBarWindowController,
+ mStatusBarWindowStateController,
mKeyguardUpdateMonitor,
mStatusBarSignalPolicy,
mPulseExpansionHandler,
@@ -449,7 +461,7 @@
mScreenOffAnimationController,
mWallpaperController,
mOngoingCallController,
- new StatusBarHideIconsForBouncerManager(mCommandQueue, mMainExecutor, mDumpManager),
+ mStatusBarHideIconsForBouncerManager,
mLockscreenTransitionController,
mFeatureFlags,
mKeyguardUnlockAnimationController,
@@ -459,7 +471,9 @@
mWallpaperManager,
Optional.of(mStartingSurface),
mActivityLaunchAnimator,
- mNotifPipelineFlags);
+ mNotifPipelineFlags,
+ mJankMonitor,
+ mDeviceStateManager);
when(mKeyguardViewMediator.registerStatusBar(
any(StatusBar.class),
any(NotificationPanelViewController.class),
@@ -942,6 +956,47 @@
verify(mStatusBarKeyguardViewManager).updateResources();
}
+ @Test
+ public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() {
+ setFoldedStates(FOLD_STATE_FOLDED);
+ setGoToSleepStates(FOLD_STATE_FOLDED);
+ when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
+
+ setDeviceState(FOLD_STATE_UNFOLDED);
+
+ verify(mStatusBarStateController).setLeaveOpenOnKeyguardHide(true);
+ }
+
+ @Test
+ public void deviceStateChange_unfolded_shadeClose_doesNotSetLeaveOpenOnKeyguardHide() {
+ setFoldedStates(FOLD_STATE_FOLDED);
+ setGoToSleepStates(FOLD_STATE_FOLDED);
+ when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(false);
+
+ setDeviceState(FOLD_STATE_UNFOLDED);
+
+ verify(mStatusBarStateController, never()).setLeaveOpenOnKeyguardHide(true);
+ }
+
+ private void setDeviceState(int state) {
+ ArgumentCaptor<DeviceStateManager.DeviceStateCallback> callbackCaptor =
+ ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
+ verify(mDeviceStateManager).registerCallback(any(), callbackCaptor.capture());
+ callbackCaptor.getValue().onStateChanged(state);
+ }
+
+ private void setGoToSleepStates(int... states) {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.array.config_deviceStatesOnWhichToSleep,
+ states);
+ }
+
+ private void setFoldedStates(int... states) {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.array.config_foldedDeviceStates,
+ states);
+ }
+
public static class TestableNotificationInterruptStateProviderImpl extends
NotificationInterruptStateProviderImpl {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index b97f053..a630840 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -37,7 +37,6 @@
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogcatEchoTracker;
@@ -57,8 +56,6 @@
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -96,6 +93,8 @@
private HeadsUpAppearanceController mHeadsUpAppearanceController;
@Mock
private NotificationPanelViewController mNotificationPanelViewController;
+ @Mock
+ private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -325,8 +324,7 @@
new PanelExpansionStateManager(),
mock(FeatureFlags.class),
mStatusBarIconController,
- new StatusBarHideIconsForBouncerManager(
- mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
+ mStatusBarHideIconsForBouncerManager,
mKeyguardStateController,
mNotificationPanelViewController,
mNetworkController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 5e852e3..d15ba26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,9 +78,15 @@
mHeadsUpManager = new TestableHeadsUpManager(mContext);
super.setUp();
+ mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
mHeadsUpManager.mHandler = mTestHandler;
}
+ @After
+ public void tearDown() {
+ mTestHandler.removeCallbacksAndMessages(null);
+ }
+
@Test
public void testShowNotification_autoDismissesWithAccessibilityTimeout() {
doReturn(TEST_A11Y_AUTO_DISMISS_TIME).when(mAccessibilityMgr)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
new file mode 100644
index 0000000..8576d4f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.window
+
+import android.app.StatusBarManager.WindowVisibleState
+import android.app.StatusBarManager.WINDOW_NAVIGATION_BAR
+import android.app.StatusBarManager.WINDOW_STATE_HIDDEN
+import android.app.StatusBarManager.WINDOW_STATE_SHOWING
+import android.app.StatusBarManager.WINDOW_STATUS_BAR
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.CommandQueue
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class StatusBarWindowStateControllerTest : SysuiTestCase() {
+ private lateinit var controller: StatusBarWindowStateController
+ private lateinit var callback: CommandQueue.Callbacks
+
+ @Mock
+ private lateinit var commandQueue: CommandQueue
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ controller = StatusBarWindowStateController(DISPLAY_ID, commandQueue)
+
+ val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
+ verify(commandQueue).addCallback(callbackCaptor.capture())
+ callback = callbackCaptor.value!!
+ }
+
+ @Test
+ fun setWindowState_notSameDisplayId_listenersNotNotified() {
+ val listener = TestListener()
+ controller.addListener(listener)
+
+ callback.setWindowState(DISPLAY_ID + 1, WINDOW_STATUS_BAR, WINDOW_STATE_HIDDEN)
+
+ assertThat(listener.state).isNull()
+ }
+
+ @Test
+ fun setWindowState_notStatusBarWindow_listenersNotNotified() {
+ val listener = TestListener()
+ controller.addListener(listener)
+
+ callback.setWindowState(DISPLAY_ID, WINDOW_NAVIGATION_BAR, WINDOW_STATE_HIDDEN)
+
+ assertThat(listener.state).isNull()
+ }
+
+ @Test
+ fun setWindowState_sameState_listenersNotNotified() {
+ val listener = TestListener()
+ controller.addListener(listener)
+
+ callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)
+
+ assertThat(listener.state).isNull()
+ }
+
+ @Test
+ fun setWindowState_newState_listenersNotified() {
+ val listener = TestListener()
+ controller.addListener(listener)
+ val newState = WINDOW_STATE_HIDDEN
+
+ callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, newState)
+
+ assertThat(listener.state).isEqualTo(newState)
+ }
+
+ private class TestListener : StatusBarWindowStateListener {
+ @WindowVisibleState var state: Int? = null
+ override fun onStatusBarWindowStateChanged(@WindowVisibleState state: Int) {
+ this.state = state
+ }
+ }
+}
+
+private const val DISPLAY_ID = 10
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index 46e0b12..f43dc6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.unfold.util.FoldableTestUtils
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
+import java.lang.Exception
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,32 +40,24 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
-import java.lang.Exception
@RunWith(AndroidTestingRunner::class)
@SmallTest
class DeviceFoldStateProviderTest : SysuiTestCase() {
- @Mock
- private lateinit var hingeAngleProvider: HingeAngleProvider
+ @Mock private lateinit var hingeAngleProvider: HingeAngleProvider
- @Mock
- private lateinit var screenStatusProvider: ScreenStatusProvider
+ @Mock private lateinit var screenStatusProvider: ScreenStatusProvider
- @Mock
- private lateinit var deviceStateManager: DeviceStateManager
+ @Mock private lateinit var deviceStateManager: DeviceStateManager
- @Mock
- private lateinit var handler: Handler
+ @Mock private lateinit var handler: Handler
- @Captor
- private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
+ @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
- @Captor
- private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
+ @Captor private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
- @Captor
- private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
+ @Captor private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
private lateinit var foldStateProvider: DeviceFoldStateProvider
@@ -81,24 +74,25 @@
MockitoAnnotations.initMocks(this)
deviceStates = FoldableTestUtils.findDeviceStates(context)
- foldStateProvider = DeviceFoldStateProvider(
- context,
- hingeAngleProvider,
- screenStatusProvider,
- deviceStateManager,
- context.mainExecutor,
- handler
- )
+ foldStateProvider =
+ DeviceFoldStateProvider(
+ context,
+ hingeAngleProvider,
+ screenStatusProvider,
+ deviceStateManager,
+ context.mainExecutor,
+ handler)
- foldStateProvider.addCallback(object : FoldStateProvider.FoldUpdatesListener {
- override fun onHingeAngleUpdate(angle: Float) {
- hingeAngleUpdates.add(angle)
- }
+ foldStateProvider.addCallback(
+ object : FoldStateProvider.FoldUpdatesListener {
+ override fun onHingeAngleUpdate(angle: Float) {
+ hingeAngleUpdates.add(angle)
+ }
- override fun onFoldUpdate(update: Int) {
- foldUpdates.add(update)
- }
- })
+ override fun onFoldUpdate(update: Int) {
+ foldUpdates.add(update)
+ }
+ })
foldStateProvider.start()
verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
@@ -200,9 +194,10 @@
sendHingeAngleEvent(90)
sendHingeAngleEvent(80)
- simulateTimeout(ABORT_CLOSING_MILLIS)
+ simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS)
- assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+ assertThat(foldUpdates)
+ .containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_FINISH_HALF_OPEN)
}
@Test
@@ -210,7 +205,7 @@
sendHingeAngleEvent(90)
sendHingeAngleEvent(80)
- simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+ simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
}
@@ -220,7 +215,7 @@
sendHingeAngleEvent(180)
sendHingeAngleEvent(90)
- simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+ simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
sendHingeAngleEvent(80)
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
@@ -231,11 +226,13 @@
sendHingeAngleEvent(180)
sendHingeAngleEvent(90)
- simulateTimeout(ABORT_CLOSING_MILLIS - 1) // The timeout should not trigger here.
+ // The timeout should not trigger here.
+ simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
sendHingeAngleEvent(80)
- simulateTimeout(ABORT_CLOSING_MILLIS) // The timeout should trigger here.
+ simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS) // The timeout should trigger here.
- assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+ assertThat(foldUpdates)
+ .containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_FINISH_HALF_OPEN)
}
@Test
@@ -261,7 +258,7 @@
}
}
- private fun simulateTimeout(waitTime: Long = ABORT_CLOSING_MILLIS) {
+ private fun simulateTimeout(waitTime: Long = HALF_OPENED_TIMEOUT_MILLIS) {
val runnableDelay = scheduledRunnableDelay ?: throw Exception("No runnable scheduled.")
if (waitTime >= runnableDelay) {
scheduledRunnable?.run()
diff --git a/services/Android.bp b/services/Android.bp
index c830c22..74d7f65 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -224,19 +224,13 @@
},
dists: [
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/system-server/api",
dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: ["sdk"],
dir: "apistubs/android/system-server/api",
dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index f050b66..ad3e1d5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -2033,6 +2033,22 @@
}
@Override
+ public void setCacheEnabled(boolean enabled) {
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setCacheEnabled", "enabled=" + enabled);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mUsesAccessibilityCache = enabled;
+ mSystemSupport.onClientChangeLocked(true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
int processId, long threadId, int callingUid, Bundle callingStack) {
if (mTrace.isA11yTracingEnabledForTypes(loggingTypes)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index a63dd00..037dc1f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -578,7 +578,7 @@
synchronized (mLock) {
if (mWindowMagnificationMgr == null) {
mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
- mUserId, this, mAms.getTraceManager(),
+ mLock, this, mAms.getTraceManager(),
mScaleProvider);
}
return mWindowMagnificationMgr;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 336f0bb..c4a577d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -87,7 +87,7 @@
})
public @interface WindowPosition {}
- private final Object mLock = new Object();
+ private final Object mLock;
private final Context mContext;
@VisibleForTesting
@GuardedBy("mLock")
@@ -152,9 +152,10 @@
private final AccessibilityTraceManager mTrace;
private final MagnificationScaleProvider mScaleProvider;
- public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback,
+ public WindowMagnificationManager(Context context, Object lock, @NonNull Callback callback,
AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) {
mContext = context;
+ mLock = lock;
mCallback = callback;
mTrace = trace;
mScaleProvider = scaleProvider;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 422749e..051281c 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -241,9 +241,6 @@
protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
@NonNull ContentObserver observer) {
resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
- UserHandle.USER_ALL);
- resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_LOGGING_LEVEL), false, observer,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
@@ -274,8 +271,6 @@
break;
default:
Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
- // fall through
- case Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
synchronized (mLock) {
updateCachedServiceLocked(userId);
}
@@ -307,6 +302,9 @@
case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT:
setDeviceConfigProperties();
break;
+ case AutofillManager.DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
+ updateCachedServices();
+ break;
default:
Slog.i(mTag, "Ignoring change on " + key);
}
@@ -588,6 +586,15 @@
}
}
+ private void updateCachedServices() {
+ List<UserInfo> supportedUsers = getSupportedUsers();
+ for (UserInfo userInfo : supportedUsers) {
+ synchronized (mLock) {
+ updateCachedServiceLocked(userInfo.id);
+ }
+ }
+ }
+
// Called by Shell command.
void calculateScore(@Nullable String algorithmName, @NonNull String value1,
@NonNull String value2, @NonNull RemoteCallback callback) {
@@ -702,31 +709,44 @@
return;
}
- final Map<String, String[]> whiteListedPackages = getWhitelistedCompatModePackages();
+ final Map<String, String[]> allowedPackages = getAllowedCompatModePackages();
final int compatPackageCount = compatPackages.size();
for (int i = 0; i < compatPackageCount; i++) {
final String packageName = compatPackages.keyAt(i);
- if (whiteListedPackages == null || !whiteListedPackages.containsKey(packageName)) {
- Slog.w(TAG, "Ignoring not whitelisted compat package " + packageName);
+ if (allowedPackages == null || !allowedPackages.containsKey(packageName)) {
+ Slog.w(TAG, "Ignoring not allowed compat package " + packageName);
continue;
}
final Long maxVersionCode = compatPackages.valueAt(i);
if (maxVersionCode != null) {
mAutofillCompatState.addCompatibilityModeRequest(packageName,
- maxVersionCode, whiteListedPackages.get(packageName), userId);
+ maxVersionCode, allowedPackages.get(packageName), userId);
}
}
}
- private String getWhitelistedCompatModePackagesFromSettings() {
+ private String getAllowedCompatModePackagesFromDeviceConfig() {
+ String config = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ AutofillManager.DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
+ /* defaultValue */ null);
+ if (!TextUtils.isEmpty(config)) {
+ return config;
+ }
+ // Fallback to Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES if
+ // the device config is null.
+ return getAllowedCompatModePackagesFromSettings();
+ }
+
+ private String getAllowedCompatModePackagesFromSettings() {
return Settings.Global.getString(
getContext().getContentResolver(),
Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES);
}
@Nullable
- private Map<String, String[]> getWhitelistedCompatModePackages() {
- return getWhitelistedCompatModePackages(getWhitelistedCompatModePackagesFromSettings());
+ private Map<String, String[]> getAllowedCompatModePackages() {
+ return getAllowedCompatModePackages(getAllowedCompatModePackagesFromDeviceConfig());
}
private void send(@NonNull IResultReceiver receiver, int value) {
@@ -771,7 +791,7 @@
@Nullable
@VisibleForTesting
- static Map<String, String[]> getWhitelistedCompatModePackages(String setting) {
+ static Map<String, String[]> getAllowedCompatModePackages(String setting) {
if (TextUtils.isEmpty(setting)) {
return null;
}
@@ -1756,8 +1776,8 @@
mUi.dump(pw);
pw.print("Autofill Compat State: ");
mAutofillCompatState.dump(prefix, pw);
- pw.print("from settings: ");
- pw.println(getWhitelistedCompatModePackagesFromSettings());
+ pw.print("from device config: ");
+ pw.println(getAllowedCompatModePackagesFromDeviceConfig());
if (mSupportedSmartSuggestionModes != 0) {
pw.print("Smart Suggestion modes: ");
pw.println(getSmartSuggestionModeToString(mSupportedSmartSuggestionModes));
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index f8da035..efa026b 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -3907,14 +3907,20 @@
}
int operationType;
+ TransportConnection transportConnection = null;
try {
- operationType = getOperationTypeFromTransport(
- mTransportManager.getTransportClientOrThrow(transport, /* caller */
- "BMS.beginRestoreSession"));
+ transportConnection = mTransportManager.getTransportClientOrThrow(
+ transport, /* caller */"BMS.beginRestoreSession");
+ operationType = getOperationTypeFromTransport(transportConnection);
} catch (TransportNotAvailableException | TransportNotRegisteredException
| RemoteException e) {
Slog.w(TAG, "Failed to get operation type from transport: " + e);
return null;
+ } finally {
+ if (transportConnection != null) {
+ mTransportManager.disposeOfTransportClient(transportConnection,
+ /* caller */"BMS.beginRestoreSession");
+ }
}
synchronized (this) {
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index e2a814b..97ec3bb 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -38,13 +38,13 @@
import android.os.Environment;
import android.util.ArrayMap;
import android.util.AtomicFile;
-import android.util.ExceptionUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -53,6 +53,7 @@
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
@@ -307,26 +308,23 @@
private void persistStateToFileLocked(@NonNull AtomicFile file,
@Nullable Collection<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
- file.write(out -> {
- try {
- final TypedXmlSerializer serializer = Xml.resolveSerializer(out);
- serializer.setFeature(
- "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ // Writing to file could fail, for example, if the user has been recently removed and so was
+ // their DE (/data/system_de/<user-id>/) directory.
+ writeToFileSafely(file, out -> {
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(out);
+ serializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startDocument(null, true);
- serializer.startTag(null, XML_TAG_STATE);
- writeIntAttribute(serializer,
- XML_ATTR_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, XML_TAG_STATE);
+ writeIntAttribute(serializer,
+ XML_ATTR_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION);
- writeAssociations(serializer, associations);
- writePreviouslyUsedIds(serializer, previouslyUsedIdsPerPackage);
+ writeAssociations(serializer, associations);
+ writePreviouslyUsedIds(serializer, previouslyUsedIdsPerPackage);
- serializer.endTag(null, XML_TAG_STATE);
- serializer.endDocument();
- } catch (Exception e) {
- Slog.e(LOG_TAG, "Error while writing associations file", e);
- throw ExceptionUtils.propagate(e);
- }
+ serializer.endTag(null, XML_TAG_STATE);
+ serializer.endDocument();
});
}
@@ -524,4 +522,13 @@
}
return associationInfo;
}
+
+ private static void writeToFileSafely(@NonNull AtomicFile file,
+ @NonNull ThrowingConsumer<FileOutputStream> consumer) {
+ try {
+ file.write(consumer);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Error while writing to file " + file, e);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index bc8da84..262933d 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -45,6 +45,7 @@
import android.bluetooth.IBluetoothManagerCallback;
import android.bluetooth.IBluetoothProfileServiceConnection;
import android.bluetooth.IBluetoothStateChangeCallback;
+import android.bluetooth.IBluetoothLeCallControl;
import android.content.ActivityNotFoundException;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
@@ -1323,11 +1324,15 @@
+ bluetoothProfile);
}
- if (bluetoothProfile != BluetoothProfile.HEADSET) {
+ Intent intent;
+ if (bluetoothProfile == BluetoothProfile.HEADSET) {
+ intent = new Intent(IBluetoothHeadset.class.getName());
+ } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) {
+ intent = new Intent(IBluetoothLeCallControl.class.getName());
+ } else {
return false;
}
- Intent intent = new Intent(IBluetoothHeadset.class.getName());
psc = new ProfileServiceConnections(intent);
if (!psc.bindService()) {
return false;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c7f4b4d..780afd8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -80,6 +80,7 @@
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
@@ -1648,7 +1649,8 @@
// obb data to its new location. This may take time depending on the size of
// the data to be copied so it's done on the StorageManager worker thread.
// This needs to be finished before start mounting obb directories.
- if (userId == 0) {
+ if (userId == 0
+ && Build.VERSION.DEVICE_INITIAL_SDK_INT < Build.VERSION_CODES.Q) {
mPmInternal.migrateLegacyObbData();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f0f6bd1..9d2b4e7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -189,6 +189,7 @@
import android.app.PropertyInvalidatedCache;
import android.app.SyncNotedAppOp;
import android.app.WaitResult;
+import android.app.WtfException;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
import android.app.compat.CompatChanges;
@@ -5822,7 +5823,7 @@
return Arrays.binarySearch(allowlist, appId) >= 0
|| Arrays.binarySearch(mDeviceIdleTempAllowlist, appId) >= 0
- || mPendingTempAllowlist.indexOfKey(uid) >= 0;
+ || mPendingTempAllowlist.get(uid) != null;
}
/**
@@ -12634,6 +12635,7 @@
int callingUid;
int callingPid;
boolean instantApp;
+ boolean throwWtfException = false;
synchronized(this) {
if (caller != null) {
callerApp = getRecordForAppLOSP(caller);
@@ -12728,13 +12730,9 @@
+ "RECEIVER_NOT_EXPORTED be specified when registering a "
+ "receiver");
} else {
- Slog.wtf(TAG,
- callerPackage + ": Targeting T+ (version "
- + Build.VERSION_CODES.TIRAMISU
- + " and above) requires that one of RECEIVER_EXPORTED or "
- + "RECEIVER_NOT_EXPORTED be specified when registering a "
- + "receiver");
+ // will be removed when enforcement is required
// Assume default behavior-- flag check is not enforced
+ throwWtfException = true;
flags |= Context.RECEIVER_EXPORTED;
}
} else if (!requireExplicitFlagForDynamicReceivers) {
@@ -12865,6 +12863,15 @@
}
}
+ if (throwWtfException) {
+ throw new WtfException(
+ callerPackage + ": Targeting T+ (version "
+ + Build.VERSION_CODES.TIRAMISU
+ + " and above) requires that one of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED be specified when registering a "
+ + "receiver");
+ }
+
return sticky;
}
}
@@ -15115,11 +15122,13 @@
// First copy out the pending changes... we need to leave them in the map for now,
// in case someone needs to check what is coming up while we don't have the lock held.
- synchronized (mProcLock) {
- N = mPendingTempAllowlist.size();
- list = new PendingTempAllowlist[N];
- for (int i = 0; i < N; i++) {
- list[i] = mPendingTempAllowlist.valueAt(i);
+ synchronized (this) {
+ synchronized (mProcLock) {
+ N = mPendingTempAllowlist.size();
+ list = new PendingTempAllowlist[N];
+ for (int i = 0; i < N; i++) {
+ list[i] = mPendingTempAllowlist.valueAt(i);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 5fc11e8..8cb2040 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -56,6 +56,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.WakeLockStats;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.GpsBatteryStats;
@@ -2596,6 +2597,20 @@
}
/**
+ * Gets a snapshot of wake lock stats
+ * @hide
+ */
+ public WakeLockStats getWakeLockStats() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BATTERY_STATS, null);
+
+ // Wait for the completion of pending works if there is any
+ awaitCompletion();
+ synchronized (mStats) {
+ return mStats.getWakeLockStats();
+ }
+ }
+
+ /**
* Gets a snapshot of the system health for a particular uid.
*/
@Override
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 0c94fbb..c55bbe8 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -920,7 +920,7 @@
void unfreezeTemporarily(ProcessRecord app) {
if (mUseFreezer) {
synchronized (mProcLock) {
- if (app.mOptRecord.isFrozen()) {
+ if (app.mOptRecord.isFrozen() || app.mOptRecord.isPendingFreeze()) {
unfreezeAppLSP(app);
freezeAppAsyncLSP(app);
}
diff --git a/services/core/java/com/android/server/am/PendingTempAllowlists.java b/services/core/java/com/android/server/am/PendingTempAllowlists.java
index 75935c4..0263de7 100644
--- a/services/core/java/com/android/server/am/PendingTempAllowlists.java
+++ b/services/core/java/com/android/server/am/PendingTempAllowlists.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.os.Process.INVALID_UID;
+
import android.util.SparseArray;
/** Allowlists of uids to temporarily bypass Power Save mode. */
@@ -31,29 +33,42 @@
}
void put(int uid, ActivityManagerService.PendingTempAllowlist value) {
- mPendingTempAllowlist.put(uid, value);
+ synchronized (mPendingTempAllowlist) {
+ mPendingTempAllowlist.put(uid, value);
+ }
mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag);
}
void removeAt(int index) {
- final int uid = mPendingTempAllowlist.keyAt(index);
- mPendingTempAllowlist.removeAt(index);
+ int uid = INVALID_UID;
+ synchronized (mPendingTempAllowlist) {
+ uid = mPendingTempAllowlist.keyAt(index);
+ mPendingTempAllowlist.removeAt(index);
+ }
mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid);
}
ActivityManagerService.PendingTempAllowlist get(int uid) {
- return mPendingTempAllowlist.get(uid);
+ synchronized (mPendingTempAllowlist) {
+ return mPendingTempAllowlist.get(uid);
+ }
}
int size() {
- return mPendingTempAllowlist.size();
+ synchronized (mPendingTempAllowlist) {
+ return mPendingTempAllowlist.size();
+ }
}
ActivityManagerService.PendingTempAllowlist valueAt(int index) {
- return mPendingTempAllowlist.valueAt(index);
+ synchronized (mPendingTempAllowlist) {
+ return mPendingTempAllowlist.valueAt(index);
+ }
}
int indexOfKey(int key) {
- return mPendingTempAllowlist.indexOfKey(key);
+ synchronized (mPendingTempAllowlist) {
+ return mPendingTempAllowlist.indexOfKey(key);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 2def50e..1ad0bce 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -135,6 +135,7 @@
import com.android.server.am.ActivityManagerService.ProcessChangeItem;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowManagerService;
@@ -2379,6 +2380,8 @@
final String[] targetPackagesList = sharedPackages.length == 0
? new String[]{app.info.packageName} : sharedPackages;
+ final boolean hasAppStorage = hasAppStorage(pmInt, app.info.packageName);
+
pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, targetPackagesList, uid);
if (pkgDataInfoMap == null) {
// TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
@@ -2401,6 +2404,12 @@
bindMountAppsData = false;
}
+ if (!hasAppStorage) {
+ bindMountAppsData = false;
+ pkgDataInfoMap = null;
+ allowlistedAppDataInfoMap = null;
+ }
+
int userId = UserHandle.getUserId(uid);
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
@@ -2488,6 +2497,17 @@
}
}
+ private boolean hasAppStorage(PackageManagerInternal pmInt, String packageName) {
+ final AndroidPackage pkg = pmInt.getPackage(packageName);
+ if (pkg == null) {
+ Slog.w(TAG, "Unknown package " + packageName);
+ return false;
+ }
+ final PackageManager.Property noAppStorageProp =
+ pkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
+ return noAppStorageProp == null || !noAppStorageProp.getBoolean();
+ }
+
@GuardedBy("mService")
void startProcessLocked(ProcessRecord app, HostingRecord hostingRecord, int zygotePolicyFlags) {
startProcessLocked(app, hostingRecord, zygotePolicyFlags, null /* abiOverride */);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 8615393..0879bec 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3239,6 +3239,13 @@
}
}
+ private void enforceQueryStatePermission() {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Missing QUERY_AUDIO_STATE permissions");
+ }
+ }
+
private void enforceQueryStateOrModifyRoutingPermission() {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED
@@ -4143,6 +4150,7 @@
/** Get last audible volume before stream was muted. */
public int getLastAudibleStreamVolume(int streamType) {
+ enforceQueryStatePermission();
ensureValidStreamType(streamType);
int device = getDeviceForStream(streamType);
return (mStreamStates[streamType].getIndex(device) + 5) / 10;
@@ -9771,7 +9779,7 @@
projection)) {
Slog.w(TAG, "Permission denied to register audio policy for pid "
+ Binder.getCallingPid() + " / uid " + Binder.getCallingUid()
- + ", need MODIFY_AUDIO_ROUTING or MediaProjection that can project audio");
+ + ", need system permission or a MediaProjection that can project audio");
return null;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 2465ec5..6f71768 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -38,8 +38,8 @@
private static final String TAG = "Biometrics/AcquisitionClient";
- private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
private static final VibrationEffect SUCCESS_VIBRATION_EFFECT =
VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
@@ -196,7 +196,7 @@
getContext().getOpPackageName(),
SUCCESS_VIBRATION_EFFECT,
getClass().getSimpleName() + "::success",
- TOUCH_VIBRATION_ATTRIBUTES);
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
}
}
@@ -207,7 +207,7 @@
getContext().getOpPackageName(),
ERROR_VIBRATION_EFFECT,
getClass().getSimpleName() + "::error",
- TOUCH_VIBRATION_ATTRIBUTES);
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
}
}
}
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index df95bf5..2a6456d 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -16,117 +16,34 @@
package com.android.server.communal;
-import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
-import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_BY_DEFAULT;
-import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-
-import static com.android.server.wm.ActivityInterceptorCallback.COMMUNAL_MODE_ORDERED_ID;
-
import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.app.KeyguardManager;
-import android.app.PendingIntent;
import android.app.communal.ICommunalManager;
import android.app.communal.ICommunalModeListener;
-import android.app.compat.CompatChanges;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.net.Uri;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.dreams.DreamManagerInternal;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.LaunchAfterAuthenticationActivity;
-import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.wm.ActivityInterceptorCallback;
-import com.android.server.wm.ActivityTaskManagerInternal;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* System service for handling Communal Mode state.
*/
public final class CommunalManagerService extends SystemService {
- private static final String TAG = CommunalManagerService.class.getSimpleName();
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String DELIMITER = ",";
private final Context mContext;
- private final ActivityTaskManagerInternal mAtmInternal;
- private final KeyguardManager mKeyguardManager;
private final AtomicBoolean mCommunalViewIsShowing = new AtomicBoolean(false);
private final BinderService mBinderService;
- private final PackageReceiver mPackageReceiver;
- private final PackageManager mPackageManager;
- private final DreamManagerInternal mDreamManagerInternal;
private final RemoteCallbackList<ICommunalModeListener> mListeners =
new RemoteCallbackList<>();
- private final ActivityInterceptorCallback mActivityInterceptorCallback =
- new ActivityInterceptorCallback() {
- @Nullable
- @Override
- public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
- if (!shouldIntercept(info.aInfo)) {
- if (DEBUG) {
- Slog.d(TAG, "Activity allowed, not intercepting: "
- + info.aInfo.getComponentName());
- }
- return null;
- }
-
- final IIntentSender target = mAtmInternal.getIntentSender(
- INTENT_SENDER_ACTIVITY,
- info.callingPackage,
- info.callingFeatureId,
- info.callingUid,
- info.userId,
- /* token= */null,
- /* resultWho= */ null,
- /* requestCode= */ 0,
- new Intent[]{info.intent},
- new String[]{info.resolvedType},
- PendingIntent.FLAG_IMMUTABLE,
- /* bOptions= */ null);
-
- return new ActivityInterceptResult(
- LaunchAfterAuthenticationActivity.createLaunchAfterAuthenticationIntent(
- new IntentSender(target)),
- info.checkedOptions);
-
- }
- };
public CommunalManagerService(Context context) {
super(context);
mContext = context;
- mPackageManager = mContext.getPackageManager();
- mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
- mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
- mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
mBinderService = new BinderService();
- mPackageReceiver = new PackageReceiver(mContext);
}
@VisibleForTesting
@@ -139,116 +56,6 @@
publishBinderService(Context.COMMUNAL_SERVICE, mBinderService);
}
- @Override
- public void onBootPhase(int phase) {
- if (phase != SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) return;
- mAtmInternal.registerActivityStartInterceptor(
- COMMUNAL_MODE_ORDERED_ID,
- mActivityInterceptorCallback);
- mPackageReceiver.register();
- removeUninstalledPackagesFromSettings();
- }
-
- @Override
- public void finalize() {
- mPackageReceiver.unregister();
- }
-
- private Set<String> getUserEnabledApps() {
- final String encodedApps = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.COMMUNAL_MODE_PACKAGES,
- UserHandle.USER_SYSTEM);
-
- return TextUtils.isEmpty(encodedApps)
- ? Collections.emptySet()
- : new HashSet<>(Arrays.asList(encodedApps.split(DELIMITER)));
- }
-
- private void removeUninstalledPackagesFromSettings() {
- for (String packageName : getUserEnabledApps()) {
- if (!isPackageInstalled(packageName, mPackageManager)) {
- removePackageFromSettings(packageName);
- }
- }
- }
-
- private void removePackageFromSettings(String packageName) {
- Set<String> enabledPackages = getUserEnabledApps();
- if (enabledPackages.remove(packageName)) {
- Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.COMMUNAL_MODE_PACKAGES,
- String.join(DELIMITER, enabledPackages),
- UserHandle.USER_SYSTEM);
- }
- }
-
- @VisibleForTesting
- static boolean isPackageInstalled(String packageName, PackageManager packageManager) {
- if (packageManager == null) return false;
- try {
- return packageManager.getPackageInfo(packageName, 0) != null;
- } catch (PackageManager.NameNotFoundException e) {
- return false;
- }
- }
-
- private boolean isAppAllowed(ApplicationInfo appInfo) {
- if (isActiveDream(appInfo) || isChangeEnabled(ALLOW_COMMUNAL_MODE_BY_DEFAULT, appInfo)) {
- return true;
- }
-
- if (!isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, appInfo)) {
- if (DEBUG) Slog.d(TAG, "App is not allowlisted: " + appInfo.packageName);
- return false;
- }
-
- if (!getUserEnabledApps().contains(appInfo.packageName)) {
- if (DEBUG) Slog.d(TAG, "App does not have user consent: " + appInfo.packageName);
- return false;
- }
-
- return true;
- }
-
- private boolean isActiveDream(ApplicationInfo appInfo) {
- final ComponentName activeDream = mDreamManagerInternal.getActiveDreamComponent(
- /* doze= */ false);
- final ComponentName activeDoze = mDreamManagerInternal.getActiveDreamComponent(
- /* doze= */ true);
- return isFromPackage(activeDream, appInfo) || isFromPackage(activeDoze, appInfo);
- }
-
- private static boolean isFromPackage(ComponentName componentName, ApplicationInfo appInfo) {
- if (componentName == null) return false;
- return TextUtils.equals(appInfo.packageName, componentName.getPackageName());
- }
-
- private static boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
- return CompatChanges.isChangeEnabled(changeId, appInfo.packageName, UserHandle.SYSTEM);
- }
-
- private boolean shouldIntercept(ActivityInfo activityInfo) {
- if (!mCommunalViewIsShowing.get() || !mKeyguardManager.isKeyguardLocked()) return false;
- ApplicationInfo appInfo = activityInfo.applicationInfo;
- // Dreams are allowed to show, and don't require the showWhenLocked attribute.
- if (isActiveDream(appInfo)) return false;
-
- // If the activity doesn't have showWhenLocked enabled, disallow the activity.
- final boolean showWhenLocked =
- (activityInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
- if (!showWhenLocked) {
- if (DEBUG) {
- Slog.d(TAG, "Activity does not contain showWhenLocked attribute: "
- + activityInfo.getComponentName());
- }
- return true;
- }
-
- return !isAppAllowed(appInfo);
- }
-
private void dispatchCommunalMode(boolean isShowing) {
synchronized (mListeners) {
int i = mListeners.beginBroadcast();
@@ -319,49 +126,4 @@
}
}
}
-
- /**
- * A {@link BroadcastReceiver} that listens on package removed events and updates any stored
- * package state in Settings.
- */
- private final class PackageReceiver extends BroadcastReceiver {
- private final Context mContext;
- private final IntentFilter mIntentFilter;
-
- private PackageReceiver(Context context) {
- mContext = context;
- mIntentFilter = new IntentFilter();
- mIntentFilter.addAction(ACTION_PACKAGE_REMOVED);
- mIntentFilter.addDataScheme("package");
- }
-
- private void register() {
- mContext.registerReceiverAsUser(
- this,
- UserHandle.SYSTEM,
- mIntentFilter,
- /* broadcastPermission= */null,
- /* scheduler= */ null);
- }
-
- private void unregister() {
- mContext.unregisterReceiver(this);
- }
-
- @Override
- public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
- final Uri data = intent.getData();
- if (data == null) {
- Slog.w(TAG, "Failed to get package name in package receiver");
- return;
- }
- final String packageName = data.getSchemeSpecificPart();
- final String action = intent.getAction();
- if (ACTION_PACKAGE_REMOVED.equals(action)) {
- removePackageFromSettings(packageName);
- } else {
- Slog.w(TAG, "Unsupported action in package receiver: " + action);
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a2fed291..6a716cb 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1461,7 +1461,10 @@
// parameters. If that fails, disconnect.
if (oldConfig != null
&& updateLinkPropertiesInPlaceIfPossible(mNetworkAgent, oldConfig)) {
- // Keep mNetworkAgent unchanged
+ // Update underlying networks if it is changed.
+ if (!Arrays.equals(oldConfig.underlyingNetworks, config.underlyingNetworks)) {
+ setUnderlyingNetworks(config.underlyingNetworks);
+ }
} else {
// Initialize the state for a new agent, while keeping the old one connected
// in case this new connection fails.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c0a6abf..c4f2b14 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -714,7 +714,6 @@
display.getDisplayInfoLocked().shouldConstrainMetricsForLauncher;
if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
handleLogicalDisplayChangedLocked(display);
- scheduleTraversalLocked(false);
}
}
}
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index d2baaf22..d632ee3 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -147,7 +147,8 @@
getContext(), getHandler(), mWifiDisplayListener);
getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
- new IntentFilter(ACTION_DISCONNECT), null, mHandler);
+ new IntentFilter(ACTION_DISCONNECT), null, mHandler,
+ Context.RECEIVER_NOT_EXPORTED);
}
});
}
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 1461675..9e00f95 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -509,7 +509,7 @@
*
* <pre><code>
* resolver.registerContentObserver(Settings.Global.getUriFor(
- * Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
+ * Settings.Global.AUTOFILL_LOGGING_LEVEL), false, observer,
* UserHandle.USER_ALL);
* </code></pre>
*
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
index 9fa6fad..773dc68 100644
--- a/services/core/java/com/android/server/input/InputShellCommand.java
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -560,7 +560,12 @@
sleep(duration);
for (KeyEvent event: events) {
- injectKeyEventAsync(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+ final int keyCode = event.getKeyCode();
+ final KeyEvent upEvent = new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode,
+ 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
+ inputSource);
+ injectKeyEventAsync(upEvent);
+ metaState &= ~MODIFIER.getOrDefault(keyCode, 0);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/ImfLock.java b/services/core/java/com/android/server/inputmethod/ImfLock.java
new file mode 100644
index 0000000..612c14f
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ImfLock.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+/**
+ * The implicit lock of this class serves as the global lock for
+ * the {@link InputMethodManagerService} and its controllers,
+ * which contain the main logic of the input method framework (IMF).
+ *
+ * <p>
+ * This lock can be used as follows in code:
+ * <pre>
+ * synchronized (ImfLock.class) {
+ * ...
+ * }
+ * </pre>
+ *
+ * <p>
+ * For annotations, you can use a similar syntax:
+ * <pre>
+ * @GuardedBy("ImfLock.class")
+ * myMethodDeclaration() {
+ * ...
+ * }
+ * </pre>
+ */
+final class ImfLock {
+ private ImfLock() {
+ // no instances
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index c5dc23e..220d790 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -72,16 +72,16 @@
@NonNull private final WindowManagerInternal mWindowManagerInternal;
@NonNull private final Resources mRes;
- private long mLastBindTime;
- private boolean mHasConnection;
- @Nullable private String mCurId;
- @Nullable private String mSelectedMethodId;
- @Nullable private Intent mCurIntent;
- @Nullable private IInputMethod mCurMethod;
- private int mCurMethodUid = Process.INVALID_UID;
- private IBinder mCurToken;
- private int mCurSeq;
- private boolean mVisibleBound;
+ @GuardedBy("ImfLock.class") private long mLastBindTime;
+ @GuardedBy("ImfLock.class") private boolean mHasConnection;
+ @GuardedBy("ImfLock.class") @Nullable private String mCurId;
+ @GuardedBy("ImfLock.class") @Nullable private String mSelectedMethodId;
+ @GuardedBy("ImfLock.class") @Nullable private Intent mCurIntent;
+ @GuardedBy("ImfLock.class") @Nullable private IInputMethod mCurMethod;
+ @GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
+ @GuardedBy("ImfLock.class") private IBinder mCurToken;
+ @GuardedBy("ImfLock.class") private int mCurSeq;
+ @GuardedBy("ImfLock.class") private boolean mVisibleBound;
private boolean mSupportsStylusHw;
/**
@@ -146,6 +146,7 @@
* Time that we last initiated a bind to the input method, to determine
* if we should try to disconnect and reconnect to it.
*/
+ @GuardedBy("ImfLock.class")
long getLastBindTime() {
return mLastBindTime;
}
@@ -154,6 +155,7 @@
* Set to true if our ServiceConnection is currently actively bound to
* a service (whether or not we have gotten its IBinder back yet).
*/
+ @GuardedBy("ImfLock.class")
boolean hasConnection() {
return mHasConnection;
}
@@ -166,6 +168,7 @@
*
* @see #getSelectedMethodId()
*/
+ @GuardedBy("ImfLock.class")
@Nullable
String getCurId() {
return mCurId;
@@ -184,11 +187,13 @@
*
* @see #getCurId()
*/
+ @GuardedBy("ImfLock.class")
@Nullable
String getSelectedMethodId() {
return mSelectedMethodId;
}
+ @GuardedBy("ImfLock.class")
void setSelectedMethodId(@Nullable String selectedMethodId) {
mSelectedMethodId = selectedMethodId;
}
@@ -197,6 +202,7 @@
* The token we have made for the currently active input method, to
* identify it in the future.
*/
+ @GuardedBy("ImfLock.class")
IBinder getCurToken() {
return mCurToken;
}
@@ -204,6 +210,7 @@
/**
* The Intent used to connect to the current input method.
*/
+ @GuardedBy("ImfLock.class")
@Nullable
Intent getCurIntent() {
return mCurIntent;
@@ -213,6 +220,7 @@
* The current binding sequence number, incremented every time there is
* a new bind performed.
*/
+ @GuardedBy("ImfLock.class")
int getSequenceNumber() {
return mCurSeq;
}
@@ -221,6 +229,7 @@
* Increase the current binding sequence number by one.
* Reset to 1 on overflow.
*/
+ @GuardedBy("ImfLock.class")
void advanceSequenceNumber() {
mCurSeq += 1;
if (mCurSeq <= 0) {
@@ -232,6 +241,7 @@
* If non-null, this is the input method service we are currently connected
* to.
*/
+ @GuardedBy("ImfLock.class")
@Nullable
IInputMethod getCurMethod() {
return mCurMethod;
@@ -240,6 +250,7 @@
/**
* If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntent()}.
*/
+ @GuardedBy("ImfLock.class")
int getCurMethodUid() {
return mCurMethodUid;
}
@@ -247,6 +258,7 @@
/**
* Indicates whether {@link #mVisibleConnection} is currently in use.
*/
+ @GuardedBy("ImfLock.class")
boolean isVisibleBound() {
return mVisibleBound;
}
@@ -254,11 +266,12 @@
/**
* Used to bring IME service up to visible adjustment while it is being shown.
*/
+ @GuardedBy("ImfLock.class")
private final ServiceConnection mVisibleConnection = new ServiceConnection() {
@Override public void onBindingDied(ComponentName name) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mVisibleBound) {
- unbindVisibleConnectionLocked();
+ unbindVisibleConnection();
}
}
}
@@ -273,17 +286,18 @@
/**
* Used to bind the IME while it is not currently being shown.
*/
+ @GuardedBy("ImfLock.class")
private final ServiceConnection mMainConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
mCurMethod = IInputMethod.Stub.asInterface(service);
- updateCurrentMethodUidLocked();
+ updateCurrentMethodUid();
if (mCurToken == null) {
Slog.w(TAG, "Service connected without a token!");
- unbindCurrentMethodLocked();
+ unbindCurrentMethod();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return;
}
@@ -304,8 +318,8 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- @GuardedBy("mMethodMap")
- private void updateCurrentMethodUidLocked() {
+ @GuardedBy("ImfLock.class")
+ private void updateCurrentMethodUid() {
final String curMethodPackage = mCurIntent.getComponent().getPackageName();
final int curMethodUid = mPackageManagerInternal.getPackageUid(
curMethodPackage, 0 /* flags */, mSettings.getCurrentUserId());
@@ -330,7 +344,7 @@
// refreshed when this method is called back. Running
// adb install -r <APK that implements the current IME>
// would be a good way to trigger such a situation.
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (DEBUG) {
Slog.v(TAG, "Service disconnected: " + name + " mCurIntent=" + mCurIntent);
}
@@ -339,7 +353,7 @@
// We consider this to be a new bind attempt, since the system
// should now try to restart the service for us.
mLastBindTime = SystemClock.uptimeMillis();
- clearCurMethodAndSessionsLocked();
+ clearCurMethodAndSessions();
mService.clearInputShowRequestLocked();
mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
}
@@ -347,35 +361,35 @@
}
};
- @GuardedBy("mMethodMap")
- void unbindCurrentMethodLocked() {
+ @GuardedBy("ImfLock.class")
+ void unbindCurrentMethod() {
if (mVisibleBound) {
- unbindVisibleConnectionLocked();
+ unbindVisibleConnection();
}
if (mHasConnection) {
- unbindMainConnectionLocked();
+ unbindMainConnection();
}
if (mCurToken != null) {
- removeCurrentTokenLocked();
+ removeCurrentToken();
mService.resetSystemUiLocked();
}
mCurId = null;
- clearCurMethodAndSessionsLocked();
+ clearCurMethodAndSessions();
}
- @GuardedBy("mMethodMap")
- private void clearCurMethodAndSessionsLocked() {
+ @GuardedBy("ImfLock.class")
+ private void clearCurMethodAndSessions() {
mService.clearClientSessionsLocked();
mCurMethod = null;
mCurMethodUid = Process.INVALID_UID;
}
- @GuardedBy("mMethodMap")
- private void removeCurrentTokenLocked() {
- int curTokenDisplayId = mService.getCurTokenDisplayId();
+ @GuardedBy("ImfLock.class")
+ private void removeCurrentToken() {
+ int curTokenDisplayId = mService.getCurTokenDisplayIdLocked();
if (DEBUG) {
Slog.v(TAG,
@@ -386,9 +400,9 @@
mCurToken = null;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
- InputBindResult bindCurrentMethodLocked() {
+ InputBindResult bindCurrentMethod() {
InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + mSelectedMethodId);
@@ -396,11 +410,11 @@
mCurIntent = createImeBindingIntent(info.getComponent());
- if (bindCurrentInputMethodServiceMainConnectionLocked()) {
+ if (bindCurrentInputMethodServiceMainConnection()) {
mCurId = info.getId();
mLastBindTime = SystemClock.uptimeMillis();
- addFreshWindowTokenLocked();
+ addFreshWindowToken();
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
null, null, mCurId, mCurSeq, false);
@@ -424,12 +438,12 @@
return intent;
}
- @GuardedBy("mMethodMap")
- private void addFreshWindowTokenLocked() {
- int displayIdToShowIme = mService.getDisplayIdToShowIme();
+ @GuardedBy("ImfLock.class")
+ private void addFreshWindowToken() {
+ int displayIdToShowIme = mService.getDisplayIdToShowImeLocked();
mCurToken = new Binder();
- mService.setCurTokenDisplayId(displayIdToShowIme);
+ mService.setCurTokenDisplayIdLocked(displayIdToShowIme);
try {
if (DEBUG) {
@@ -444,20 +458,20 @@
}
}
- @GuardedBy("mMethodMap")
- private void unbindMainConnectionLocked() {
+ @GuardedBy("ImfLock.class")
+ private void unbindMainConnection() {
mContext.unbindService(mMainConnection);
mHasConnection = false;
}
- @GuardedBy("mMethodMap")
- void unbindVisibleConnectionLocked() {
+ @GuardedBy("ImfLock.class")
+ void unbindVisibleConnection() {
mContext.unbindService(mVisibleConnection);
mVisibleBound = false;
}
- @GuardedBy("mMethodMap")
- private boolean bindCurrentInputMethodServiceLocked(ServiceConnection conn, int flags) {
+ @GuardedBy("ImfLock.class")
+ private boolean bindCurrentInputMethodService(ServiceConnection conn, int flags) {
if (mCurIntent == null || conn == null) {
Slog.e(TAG, "--- bind failed: service = " + mCurIntent + ", conn = " + conn);
return false;
@@ -466,16 +480,16 @@
new UserHandle(mSettings.getCurrentUserId()));
}
- @GuardedBy("mMethodMap")
- private boolean bindCurrentInputMethodServiceVisibleConnectionLocked() {
- mVisibleBound = bindCurrentInputMethodServiceLocked(mVisibleConnection,
+ @GuardedBy("ImfLock.class")
+ private boolean bindCurrentInputMethodServiceVisibleConnection() {
+ mVisibleBound = bindCurrentInputMethodService(mVisibleConnection,
IME_VISIBLE_BIND_FLAGS);
return mVisibleBound;
}
- @GuardedBy("mMethodMap")
- private boolean bindCurrentInputMethodServiceMainConnectionLocked() {
- mHasConnection = bindCurrentInputMethodServiceLocked(mMainConnection,
+ @GuardedBy("ImfLock.class")
+ private boolean bindCurrentInputMethodServiceMainConnection() {
+ mHasConnection = bindCurrentInputMethodService(mMainConnection,
mImeConnectionBindFlags);
return mHasConnection;
}
@@ -486,27 +500,36 @@
* <p>
* Performs a rebind if no binding is achieved in {@link #TIME_TO_RECONNECT} milliseconds.
*/
- @GuardedBy("mMethodMap")
- void setCurrentMethodVisibleLocked() {
+ @GuardedBy("ImfLock.class")
+ void setCurrentMethodVisible() {
if (mCurMethod != null) {
- if (DEBUG) Slog.d(TAG, "setCurrentMethodVisibleLocked: mCurToken=" + mCurToken);
+ if (DEBUG) Slog.d(TAG, "setCurrentMethodVisible: mCurToken=" + mCurToken);
if (mHasConnection && !mVisibleBound) {
- bindCurrentInputMethodServiceVisibleConnectionLocked();
+ bindCurrentInputMethodServiceVisibleConnection();
}
return;
}
+ // No IME is currently connected. Reestablish the main connection.
+ if (!mHasConnection) {
+ if (DEBUG) {
+ Slog.d(TAG, "Cannot show input: no IME bound. Rebinding.");
+ }
+ bindCurrentMethod();
+ return;
+ }
+
long bindingDuration = SystemClock.uptimeMillis() - mLastBindTime;
- if (mHasConnection && bindingDuration >= TIME_TO_RECONNECT) {
+ if (bindingDuration >= TIME_TO_RECONNECT) {
// The client has asked to have the input method shown, but
// we have been sitting here too long with a connection to the
// service and no interface received, so let's disconnect/connect
// to try to prod things along.
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(),
bindingDuration, 1);
- Slog.w(TAG, "Force disconnect/connect to the IME in setCurrentMethodVisibleLocked()");
- unbindMainConnectionLocked();
- bindCurrentInputMethodServiceMainConnectionLocked();
+ Slog.w(TAG, "Force disconnect/connect to the IME in setCurrentMethodVisible()");
+ unbindMainConnection();
+ bindCurrentInputMethodServiceMainConnection();
} else {
if (DEBUG) {
Slog.d(TAG, "Can't show input: connection = " + mHasConnection + ", time = "
@@ -518,10 +541,10 @@
/**
* Remove the binding needed for the IME to be shown.
*/
- @GuardedBy("mMethodMap")
- void setCurrentMethodNotVisibleLocked() {
+ @GuardedBy("ImfLock.class")
+ void setCurrentMethodNotVisible() {
if (mVisibleBound) {
- unbindVisibleConnectionLocked();
+ unbindVisibleConnection();
}
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 262ab93..c87dc89 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -262,6 +262,16 @@
private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
"com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
+ /**
+ * When set, {@link #startInputUncheckedLocked} will return
+ * {@link InputBindResult#NO_EDITOR} instead of starting an IME connection
+ * unless {@link StartInputFlags#IS_TEXT_EDITOR} is set. This behavior overrides
+ * {@link LayoutParams#SOFT_INPUT_STATE_VISIBLE SOFT_INPUT_STATE_VISIBLE} and
+ * {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE SOFT_INPUT_STATE_ALWAYS_VISIBLE}
+ * starting from {@link android.os.Build.VERSION_CODES#P}.
+ */
+ private final boolean mPreventImeStartupUnlessTextEditor;
+
@UserIdInt
private int mLastSwitchUserId;
@@ -293,9 +303,7 @@
@Nullable
private AudioManagerInternal mAudioManagerInternal = null;
-
- // All known input methods. mMethodMap also serves as the global
- // lock for this class.
+ // All known input methods.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
final InputMethodSubtypeSwitchingController mSwitchingController;
@@ -303,18 +311,18 @@
/**
* Tracks how many times {@link #mMethodMap} was updated.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private int mMethodMapUpdateCount = 0;
/**
* The display id for which the latest startInput was called.
*/
- @GuardedBy("mMethodMap")
- int getDisplayIdToShowIme() {
+ @GuardedBy("ImfLock.class")
+ int getDisplayIdToShowImeLocked() {
return mDisplayIdToShowIme;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private int mDisplayIdToShowIme = INVALID_DISPLAY;
// Ongoing notification
@@ -400,7 +408,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
/**
@@ -416,17 +424,19 @@
* <p>This can be transiently {@code null} when the system is re-initializing input method
* settings, e.g., the system locale is just changed.</p>
*
- * <p>Note that {@link InputMethodBindingController#getCurId()} is used to track which IME is
- * being connected to {@link InputMethodManagerService}.</p>
+ * <p>Note that {@link InputMethodBindingController#getCurId()} is used to track which IME
+ * is being connected to {@link InputMethodManagerService}.</p>
*
* @see InputMethodBindingController#getCurId()
*/
+ @GuardedBy("ImfLock.class")
@Nullable
- private String getSelectedMethodId() {
+ String getSelectedMethodIdLocked() {
return mBindingController.getSelectedMethodId();
}
- private void setSelectedMethodId(@Nullable String selectedMethodId) {
+ @GuardedBy("ImfLock.class")
+ private void setSelectedMethodIdLocked(@Nullable String selectedMethodId) {
mBindingController.setSelectedMethodId(selectedMethodId);
}
@@ -434,7 +444,8 @@
* The current binding sequence number, incremented every time there is
* a new bind performed.
*/
- private int getSequenceNumber() {
+ @GuardedBy("ImfLock.class")
+ private int getSequenceNumberLocked() {
return mBindingController.getSequenceNumber();
}
@@ -442,7 +453,8 @@
* Increase the current binding sequence number by one.
* Reset to 1 on overflow.
*/
- private void advanceSequenceNumber() {
+ @GuardedBy("ImfLock.class")
+ private void advanceSequenceNumberLocked() {
mBindingController.advanceSequenceNumber();
}
@@ -501,10 +513,11 @@
*
* <p>This can be {@code null} when no input method is connected.</p>
*
- * @see #getSelectedMethodId()
+ * @see #getSelectedMethodIdLocked()
*/
+ @GuardedBy("ImfLock.class")
@Nullable
- private String getCurId() {
+ private String getCurIdLocked() {
return mBindingController.getCurId();
}
@@ -522,7 +535,8 @@
* Set to true if our ServiceConnection is currently actively bound to
* a service (whether or not we have gotten its IBinder back yet).
*/
- private boolean hasConnection() {
+ @GuardedBy("ImfLock.class")
+ private boolean hasConnectionLocked() {
return mBindingController.hasConnection();
}
@@ -554,8 +568,9 @@
/**
* The Intent used to connect to the current input method.
*/
+ @GuardedBy("ImfLock.class")
@Nullable
- private Intent getCurIntent() {
+ private Intent getCurIntentLocked() {
return mBindingController.getCurIntent();
}
@@ -563,27 +578,31 @@
* The token we have made for the currently active input method, to
* identify it in the future.
*/
- private IBinder getCurToken() {
+ @GuardedBy("ImfLock.class")
+ private IBinder getCurTokenLocked() {
return mBindingController.getCurToken();
}
/**
* The displayId of current active input method.
*/
- int getCurTokenDisplayId() {
+ @GuardedBy("ImfLock.class")
+ int getCurTokenDisplayIdLocked() {
return mCurTokenDisplayId;
}
- void setCurTokenDisplayId(int curTokenDisplayId) {
+ @GuardedBy("ImfLock.class")
+ void setCurTokenDisplayIdLocked(int curTokenDisplayId) {
mCurTokenDisplayId = curTokenDisplayId;
}
- int mCurTokenDisplayId = INVALID_DISPLAY;
+ @GuardedBy("ImfLock.class")
+ private int mCurTokenDisplayId = INVALID_DISPLAY;
/**
* The host input token of the current active input method.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@Nullable
private IBinder mCurHostInputToken;
@@ -599,15 +618,17 @@
* If non-null, this is the input method service we are currently connected
* to.
*/
+ @GuardedBy("ImfLock.class")
@Nullable
- private IInputMethod getCurMethod() {
+ private IInputMethod getCurMethodLocked() {
return mBindingController.getCurMethod();
}
/**
- * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntent()}.
+ * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntentLocked()}.
*/
- private int getCurMethodUid() {
+ @GuardedBy("ImfLock.class")
+ private int getCurMethodUidLocked() {
return mBindingController.getCurMethodUid();
}
@@ -615,7 +636,8 @@
* Time that we last initiated a bind to the input method, to determine
* if we should try to disconnect and reconnect to it.
*/
- private long getLastBindTime() {
+ @GuardedBy("ImfLock.class")
+ private long getLastBindTimeLocked() {
return mBindingController.getLastBindTime();
}
@@ -658,7 +680,7 @@
* </dd>
* </dl>
* <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
- * {@link InputMethodBindingController#unbindCurrentMethodLocked()}.</em>
+ * {@link InputMethodBindingController#unbindCurrentMethod()}.</em>
*/
int mImeWindowVis;
@@ -741,7 +763,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
private static final class SoftInputShowHideHistory {
@@ -849,7 +871,7 @@
* {@link InputMethodManager#showSoftInput(View, int)}.
* This map tracks origin of showSoftInput requests.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private final WeakHashMap<IBinder, IBinder> mShowRequestWindowMap = new WeakHashMap<>();
/**
@@ -857,7 +879,7 @@
* {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}.
* This map tracks origin of hideSoftInput requests.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private final WeakHashMap<IBinder, IBinder> mHideRequestWindowMap = new WeakHashMap<>();
/**
@@ -1014,11 +1036,11 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
private final StartInputHistory mStartInputHistory = new StartInputHistory();
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
private final SoftInputShowHideHistory mSoftInputShowHideHistory =
new SoftInputShowHideHistory();
@@ -1036,7 +1058,7 @@
super(handler);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
public void registerContentObserverLocked(@UserIdInt int userId) {
if (mRegistered && mUserId == userId) {
return;
@@ -1068,7 +1090,7 @@
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (showImeUri.equals(uri)) {
mMenuController.updateKeyboardFromSettingsLocked();
} else if (accessibilityRequestingNoImeUri.equals(uri)) {
@@ -1174,7 +1196,7 @@
* <p>Caution: This method must not be called when system is not ready.</p>
*/
void onActionLocaleChanged() {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final LocaleList possibleNewLocale = mRes.getConfiguration().getLocales();
if (possibleNewLocale != null && possibleNewLocale.equals(mLastSystemLocales)) {
return;
@@ -1196,7 +1218,7 @@
* dynamically unless the entire package is updated, which also always triggers package
* rescanning.</p>
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
final private ArraySet<String> mKnownImePackageNames = new ArraySet<>();
/**
@@ -1219,17 +1241,17 @@
*/
private boolean mImePackageAppeared = false;
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void clearKnownImePackageNamesLocked() {
mKnownImePackageNames.clear();
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
final void addKnownImePackageNameLocked(@NonNull String packageName) {
mKnownImePackageNames.add(packageName);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean isChangingPackagesOfCurrentUserLocked() {
final int userId = getChangingUserId();
final boolean retval = userId == mSettings.getCurrentUserId();
@@ -1243,7 +1265,7 @@
@Override
public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!isChangingPackagesOfCurrentUserLocked()) {
return false;
}
@@ -1331,7 +1353,7 @@
mImePackageAppeared = false;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean shouldRebuildInputMethodListLocked() {
// This method is guaranteed to be called only by getRegisteredHandler().
@@ -1355,7 +1377,7 @@
}
private void onFinishPackageChangesInternal() {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!isChangingPackagesOfCurrentUserLocked()) {
return;
}
@@ -1478,7 +1500,7 @@
@Override
public void run() {
- synchronized (mService.mMethodMap) {
+ synchronized (ImfLock.class) {
if (mService.mUserSwitchHandlerTask != this) {
// This task was already canceled before it is handled here. So do nothing.
return;
@@ -1495,7 +1517,7 @@
* a handler callback. This needs to be set and unset only within the lock.
*/
@Nullable
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private UserSwitchHandlerTask mUserSwitchHandlerTask;
public static final class Lifecycle extends SystemService {
@@ -1517,7 +1539,7 @@
@Override
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
// Called on ActivityManager thread.
- synchronized (mService.mMethodMap) {
+ synchronized (ImfLock.class) {
mService.scheduleSwitchUserTaskLocked(to.getUserIdentifier(),
/* clientToBeReset= */ null);
}
@@ -1543,7 +1565,7 @@
}
void onUnlockUser(@UserIdInt int userId) {
- synchronized(mMethodMap) {
+ synchronized (ImfLock.class) {
final int currentUserId = mSettings.getCurrentUserId();
if (DEBUG) {
Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + currentUserId);
@@ -1560,7 +1582,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void scheduleSwitchUserTaskLocked(@UserIdInt int userId,
@Nullable IInputMethodClient clientToBeReset) {
if (mUserSwitchHandlerTask != null) {
@@ -1649,12 +1671,14 @@
mSettings, context);
mMenuController = new InputMethodMenuController(this);
mBindingController = new InputMethodBindingController(this);
+ mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
+ com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void resetDefaultImeLocked(Context context) {
// Do not reset the default (current) IME when it is a 3rd-party IME
- String selectedMethodId = getSelectedMethodId();
+ String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId != null && !mMethodMap.get(selectedMethodId).isSystem()) {
return;
}
@@ -1671,7 +1695,7 @@
setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void switchUserOnHandlerLocked(@UserIdInt int newUserId,
IInputMethodClient clientToBeReset) {
if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
@@ -1757,7 +1781,7 @@
}
public void systemRunning(StatusBarManagerService statusBar) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (DEBUG) {
Slog.d(TAG, "--- systemReady");
}
@@ -1813,7 +1837,7 @@
// Check whether or not this is a valid IPC. Assumes an IPC is valid when either
// 1) it comes from the system process
// 2) the calling process' user id is identical to the current user id IMMS thinks.
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean calledFromValidUserLocked() {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
@@ -1857,12 +1881,12 @@
* @param token The window token given to the input method when it was started.
* @return true if and only if non-null valid token is specified.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean calledWithValidTokenLocked(@NonNull IBinder token) {
if (token == null) {
throw new InvalidParameterException("token must not be null.");
}
- if (token != getCurToken()) {
+ if (token != getCurTokenLocked()) {
Slog.e(TAG, "Ignoring " + Debug.getCaller() + " due to an invalid token."
+ " uid:" + Binder.getCallingUid() + " token:" + token);
return false;
@@ -1876,7 +1900,7 @@
mContext.enforceCallingPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
mSettings.getCurrentUserId(), null);
if (resolvedUserIds.length != 1) {
@@ -1908,7 +1932,7 @@
mContext.enforceCallingPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
mSettings.getCurrentUserId(), null);
if (resolvedUserIds.length != 1) {
@@ -1923,7 +1947,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId,
@DirectBootAwareness int directBootAwareness) {
final ArrayList<InputMethodInfo> methodList;
@@ -1943,7 +1967,7 @@
return methodList;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId) {
if (userId == mSettings.getCurrentUserId()) {
return mSettings.getEnabledInputMethodListLocked();
@@ -1954,18 +1978,19 @@
return settings.getEnabledInputMethodListLocked();
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback) {
- final InputMethodInfo imi = mMethodMap.get(getSelectedMethodId());
+ final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
try {
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
if (userId == mSettings.getCurrentUserId() && imi != null
&& imi.isInlineSuggestionsEnabled() && curMethod != null) {
executeOrSendMessage(curMethod,
mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, curMethod,
requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback,
- imi.getPackageName(), mCurTokenDisplayId, getCurToken(),
+ imi.getPackageName(), mCurTokenDisplayId,
+ getCurTokenLocked(),
this)));
} else {
callback.onInlineSuggestionsUnsupported();
@@ -2061,7 +2086,7 @@
* @param hostInputToken the host input token of the current active input method
*/
void setCurHostInputToken(@NonNull IBinder callerImeToken, @Nullable IBinder hostInputToken) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(callerImeToken)) {
return;
}
@@ -2080,7 +2105,7 @@
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
boolean allowsImplicitlySelectedSubtypes) {
final int callingUserId = UserHandle.getCallingUserId();
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
mSettings.getCurrentUserId(), null);
if (resolvedUserIds.length != 1) {
@@ -2096,12 +2121,12 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
boolean allowsImplicitlySelectedSubtypes, @UserIdInt int userId) {
if (userId == mSettings.getCurrentUserId()) {
final InputMethodInfo imi;
- String selectedMethodId = getSelectedMethodId();
+ String selectedMethodId = getSelectedMethodIdLocked();
if (imiId == null && selectedMethodId != null) {
imi = mMethodMap.get(selectedMethodId);
} else {
@@ -2151,7 +2176,7 @@
// actually running.
final int callerUid = Binder.getCallingUid();
final int callerPid = Binder.getCallingPid();
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
// TODO: Optimize this linear search.
final int numClients = mClients.size();
for (int i = 0; i < numClients; ++i) {
@@ -2184,7 +2209,7 @@
}
void removeClient(IInputMethodClient client) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
ClientState cs = mClients.remove(client.asBinder());
if (cs != null) {
client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
@@ -2194,7 +2219,7 @@
mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
if (mBoundToMethod) {
mBoundToMethod = false;
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
if (curMethod != null) {
executeOrSendMessage(curMethod, mCaller.obtainMessageO(
MSG_UNBIND_INPUT, curMethod));
@@ -2218,14 +2243,14 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
if (mCurClient != null) {
if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client="
+ mCurClient.client.asBinder());
if (mBoundToMethod) {
mBoundToMethod = false;
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
if (curMethod != null) {
executeOrSendMessage(curMethod, mCaller.obtainMessageO(
MSG_UNBIND_INPUT, curMethod));
@@ -2235,7 +2260,8 @@
scheduleSetActiveToClient(mCurClient, false /* active */, false /* fullscreen */,
false /* reportToImeController */);
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
- MSG_UNBIND_CLIENT, getSequenceNumber(), unbindClientReason, mCurClient.client));
+ MSG_UNBIND_CLIENT, getSequenceNumberLocked(), unbindClientReason,
+ mCurClient.client));
mCurClient.sessionRequested = false;
mCurClient = null;
@@ -2243,13 +2269,13 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void clearInputShowRequestLocked() {
mShowRequested = mInputShown;
mInputShown = false;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private int getImeShowFlagsLocked() {
int flags = 0;
if (mShowForced) {
@@ -2261,7 +2287,7 @@
return flags;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private int getAppShowFlagsLocked() {
int flags = 0;
if (mShowForced) {
@@ -2272,22 +2298,23 @@
return flags;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
if (!mBoundToMethod) {
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
executeOrSendMessage(curMethod, mCaller.obtainMessageOO(
MSG_BIND_INPUT, curMethod, mCurClient.binding));
mBoundToMethod = true;
}
final Binder startInputToken = new Binder();
- final StartInputInfo info = new StartInputInfo(mSettings.getCurrentUserId(), getCurToken(),
- mCurTokenDisplayId, getCurId(), startInputReason, !initial,
+ final StartInputInfo info = new StartInputInfo(mSettings.getCurrentUserId(),
+ getCurTokenLocked(),
+ mCurTokenDisplayId, getCurIdLocked(), startInputReason, !initial,
UserHandle.getUserId(mCurClient.uid), mCurClient.selfReportedDisplayId,
mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
- getSequenceNumber());
+ getSequenceNumberLocked());
mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
mStartInputHistory.addEntry(info);
@@ -2298,7 +2325,7 @@
// INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
if (mSettings.getCurrentUserId() == UserHandle.getUserId(mCurClient.uid)) {
mPackageManagerInternal.grantImplicitAccess(mSettings.getCurrentUserId(),
- null /* intent */, UserHandle.getAppId(getCurMethodUid()), mCurClient.uid,
+ null /* intent */, UserHandle.getAppId(getCurMethodUidLocked()), mCurClient.uid,
true /* direct */);
}
@@ -2312,22 +2339,22 @@
SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
- String curId = getCurId();
+ String curId = getCurIdLocked();
final InputMethodInfo curInputMethodInfo = mMethodMap.get(curId);
final boolean suppressesSpellChecker =
curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
- curId, getSequenceNumber(), suppressesSpellChecker);
+ curId, getSequenceNumberLocked(), suppressesSpellChecker);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
@NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
- @StartInputReason int startInputReason) {
+ @StartInputReason int startInputReason, int unverifiedTargetSdkVersion) {
// If no method is currently selected, do nothing.
- String selectedMethodId = getSelectedMethodId();
+ String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId == null) {
return InputBindResult.NO_IME;
}
@@ -2337,7 +2364,7 @@
// party code.
return new InputBindResult(
InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
- null, null, selectedMethodId, getSequenceNumber(), false);
+ null, null, selectedMethodId, getSequenceNumberLocked(), false);
}
if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
@@ -2369,15 +2396,27 @@
}
// Bump up the sequence for this client and attach it.
- advanceSequenceNumber();
+ advanceSequenceNumberLocked();
mCurClient = cs;
mCurInputContext = inputContext;
mCurAttribute = attribute;
+ // If configured, we want to avoid starting up the IME if it is not supposed to be showing
+ if (mPreventImeStartupUnlessTextEditor
+ && !InputMethodUtils.isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion,
+ startInputFlags)
+ && !mShowRequested) {
+ if (DEBUG) {
+ Slog.d(TAG, "Avoiding IME startup and unbinding current input method.");
+ }
+ mBindingController.unbindCurrentMethod();
+ return InputBindResult.NO_EDITOR;
+ }
+
// Check if the input method is changing.
// We expect the caller has already verified that the client is allowed to access this
// display ID.
- if (isSelectedMethodBound()) {
+ if (isSelectedMethodBoundLocked()) {
if (cs.curSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -2391,18 +2430,19 @@
}
}
- mBindingController.unbindCurrentMethodLocked();
+ mBindingController.unbindCurrentMethod();
- return mBindingController.bindCurrentMethodLocked();
+ return mBindingController.bindCurrentMethod();
}
- private boolean isSelectedMethodBound() {
- String curId = getCurId();
- return curId != null && curId.equals(getSelectedMethodId())
+ @GuardedBy("ImfLock.class")
+ private boolean isSelectedMethodBoundLocked() {
+ String curId = getCurIdLocked();
+ return curId != null && curId.equals(getSelectedMethodIdLocked())
&& mDisplayIdToShowIme == mCurTokenDisplayId;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void prepareClientSwitchLocked(ClientState cs) {
// If the client is changing, we need to switch over to the new
// one.
@@ -2414,19 +2454,19 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@Nullable
private InputBindResult tryReuseConnectionLocked(@NonNull ClientState cs) {
- if (hasConnection()) {
- if (getCurMethod() != null) {
+ if (hasConnectionLocked()) {
+ if (getCurMethodLocked() != null) {
// Return to client, and we will get back with it when
// we have had a session made for it.
requestClientSessionLocked(cs);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
- null, null, getCurId(), getSequenceNumber(), false);
+ null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
} else {
- long bindingDuration = SystemClock.uptimeMillis() - getLastBindTime();
+ long bindingDuration = SystemClock.uptimeMillis() - getLastBindTimeLocked();
if (bindingDuration < TIME_TO_RECONNECT) {
// In this case we have connected to the service, but
// don't yet have its interface. If it hasn't been too
@@ -2437,10 +2477,10 @@
// to see if we can get back in touch with the service.
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, getCurId(), getSequenceNumber(), false);
+ null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
- getSelectedMethodId(), bindingDuration, 0);
+ getSelectedMethodIdLocked(), bindingDuration, 0);
}
}
}
@@ -2487,13 +2527,13 @@
void onSessionCreated(IInputMethod method, IInputMethodSession session,
InputChannel channel) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mUserSwitchHandlerTask != null) {
// We have a pending user-switching task so it's better to just ignore this session.
channel.dispose();
return;
}
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
if (curMethod != null && method != null
&& curMethod.asBinder() == method.asBinder()) {
if (mCurClient != null) {
@@ -2515,7 +2555,7 @@
channel.dispose();
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void resetSystemUiLocked() {
// Set IME window status as invisible when unbinding current method.
mImeWindowVis = 0;
@@ -2525,14 +2565,14 @@
mCurHostInputToken = null;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
- setSelectedMethodId(null);
- mBindingController.unbindCurrentMethodLocked();
+ setSelectedMethodIdLocked(null);
+ mBindingController.unbindCurrentMethod();
unbindCurrentClientLocked(unbindClientReason);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void reRequestCurrentClientSessionLocked() {
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
@@ -2540,27 +2580,27 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {
if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
cs.sessionRequested = true;
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
executeOrSendMessage(curMethod, mCaller.obtainMessageOOO(
MSG_CREATE_SESSION, curMethod, channels[1],
new MethodCallback(this, curMethod, channels[0])));
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void clearClientSessionLocked(ClientState cs) {
finishSessionLocked(cs.curSession);
cs.curSession = null;
cs.sessionRequested = false;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void finishSessionLocked(SessionState sessionState) {
if (sessionState != null) {
if (sessionState.session != null) {
@@ -2579,9 +2619,9 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void clearClientSessionsLocked() {
- if (getCurMethod() != null) {
+ if (getCurMethodLocked() != null) {
final int numClients = mClients.size();
for (int i = 0; i < numClients; ++i) {
clearClientSessionLocked(mClients.valueAt(i));
@@ -2598,7 +2638,7 @@
@BinderThread
private void updateStatusIcon(@NonNull IBinder token, String packageName,
@DrawableRes int iconId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -2632,14 +2672,14 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void hideStatusBarIconLocked() {
if (mStatusBar != null) {
mStatusBar.setIconVisibility(mSlotIme, false);
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean shouldShowImeSwitcherLocked(int visibility) {
if (!mShowOngoingImeSwitcherForPhones) return false;
if (mMenuController.getSwitchingDialogLocked() != null) return false;
@@ -2708,7 +2748,7 @@
private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) {
final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId();
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -2743,7 +2783,7 @@
@BinderThread
private void reportStartInput(@NonNull IBinder token, IBinder startInputToken) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -2756,7 +2796,7 @@
}
private void updateImeWindowStatus(boolean disableImeIcon) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (disableImeIcon) {
updateSystemUiLocked(0, mBackDisposition);
} else {
@@ -2765,15 +2805,15 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void updateSystemUiLocked() {
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
}
// Caution! This method is called in this class. Handle multi-user carefully
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void updateSystemUiLocked(int vis, int backDisposition) {
- if (getCurToken() == null) {
+ if (getCurTokenLocked() == null) {
return;
}
if (DEBUG) {
@@ -2798,10 +2838,10 @@
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
if (mStatusBar != null) {
- mStatusBar.setImeWindowStatus(mCurTokenDisplayId, getCurToken(), vis,
+ mStatusBar.setImeWindowStatus(mCurTokenDisplayId, getCurTokenLocked(), vis,
backDisposition, needsToShowImeSwitcher);
}
- final InputMethodInfo imi = mMethodMap.get(getSelectedMethodId());
+ final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
if (imi != null && needsToShowImeSwitcher) {
// Used to load label
final CharSequence title = mRes.getText(
@@ -2840,13 +2880,13 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void updateFromSettingsLocked(boolean enabledMayChange) {
updateInputMethodsFromSettingsLocked(enabledMayChange);
mMenuController.updateKeyboardFromSettingsLocked();
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
if (enabledMayChange) {
List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
@@ -2901,7 +2941,7 @@
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void setInputMethodLocked(String id, int subtypeId) {
InputMethodInfo info = mMethodMap.get(id);
if (info == null) {
@@ -2909,7 +2949,7 @@
}
// See if we need to notify a subtype change within the same IME.
- if (id.equals(getSelectedMethodId())) {
+ if (id.equals(getSelectedMethodIdLocked())) {
final int subtypeCount = info.getSubtypeCount();
if (subtypeCount <= 0) {
return;
@@ -2930,7 +2970,7 @@
}
if (newSubtype != oldSubtype) {
setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
if (curMethod != null) {
try {
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
@@ -2952,7 +2992,7 @@
// mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
// because mCurMethodId is stored as a history in
// setSelectedInputMethodAndSubtypeLocked().
- setSelectedMethodId(id);
+ setSelectedMethodIdLocked(id);
if (LocalServices.getService(ActivityManagerInternal.class).isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -2973,7 +3013,7 @@
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#showSoftInput");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledFromValidUserLocked()) {
return false;
}
@@ -3009,7 +3049,7 @@
public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
Objects.requireNonNull(windowToken, "windowToken must not be null");
int uid = Binder.getCallingUid();
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledFromValidUserLocked()) {
return;
}
@@ -3026,7 +3066,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
mShowRequested = true;
@@ -3045,12 +3085,12 @@
return false;
}
- mBindingController.setCurrentMethodVisibleLocked();
- if (getCurMethod() != null) {
+ mBindingController.setCurrentMethodVisible();
+ if (getCurMethodLocked() != null) {
// create a placeholder token for IMS so that IMS cannot inject windows into client app.
Binder showInputToken = new Binder();
mShowRequestWindowMap.put(showInputToken, windowToken);
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
executeOrSendMessage(curMethod, mCaller.obtainMessageIIOOO(MSG_SHOW_SOFT_INPUT,
getImeShowFlagsLocked(), reason, curMethod, resultReceiver,
showInputToken));
@@ -3067,7 +3107,7 @@
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#hideSoftInput");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!InputMethodManagerService.this.calledFromValidUserLocked()) {
return false;
}
@@ -3104,7 +3144,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
@@ -3125,7 +3165,7 @@
// since Android Eclair. That's why we need to accept IMM#hideSoftInput() even when only
// IMMS#InputShown indicates that the software keyboard is shown.
// TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
final boolean shouldHideSoftInput = (curMethod != null) && (mInputShown
|| (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
boolean res;
@@ -3142,7 +3182,7 @@
} else {
res = false;
}
- mBindingController.setCurrentMethodNotVisibleLocked();
+ mBindingController.setCurrentMethodNotVisible();
mInputShown = false;
mShowRequested = false;
mShowExplicitlyRequested = false;
@@ -3198,7 +3238,7 @@
userId = callingUserId;
}
final InputBindResult result;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final long ident = Binder.clearCallingIdentity();
try {
result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
@@ -3223,7 +3263,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
private InputBindResult startInputOrWindowGainedFocusInternalLocked(
@StartInputReason int startInputReason, IInputMethodClient client,
@@ -3316,7 +3356,7 @@
}
if (attribute != null) {
return startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
- startInputReason);
+ startInputReason, unverifiedTargetSdkVersion);
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
@@ -3357,7 +3397,7 @@
if (isTextEditor && attribute != null
&& shouldRestoreImeVisibility(windowToken, softInputMode)) {
res = startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
- startInputReason);
+ startInputReason, unverifiedTargetSdkVersion);
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
return res;
@@ -3381,7 +3421,7 @@
// Note that we can trust client's display ID as long as it matches
// to the display ID obtained from the window.
if (cs.selfReportedDisplayId != mCurTokenDisplayId) {
- mBindingController.unbindCurrentMethodLocked();
+ mBindingController.unbindCurrentMethod();
}
}
} else if (isTextEditor && doAutoShow
@@ -3396,7 +3436,7 @@
if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason);
+ startInputFlags, startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -3427,7 +3467,7 @@
unverifiedTargetSdkVersion, startInputFlags)) {
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason);
+ startInputFlags, startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -3446,7 +3486,7 @@
if (!sameWindowFocused) {
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason);
+ startInputFlags, startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -3475,7 +3515,7 @@
}
}
res = startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
- startInputReason);
+ startInputReason, unverifiedTargetSdkVersion);
} else {
res = InputBindResult.NULL_EDITOR_INFO;
}
@@ -3496,17 +3536,17 @@
return mWindowManagerInternal.shouldRestoreImeVisibility(windowToken);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
// TODO(yukawa): multi-display support.
final int uid = Binder.getCallingUid();
if (mCurFocusedWindowClient != null && client != null
&& mCurFocusedWindowClient.client.asBinder() == client.asBinder()) {
return true;
- } else if (getCurIntent() != null && InputMethodUtils.checkIfPackageBelongsToUid(
+ } else if (getCurIntentLocked() != null && InputMethodUtils.checkIfPackageBelongsToUid(
mAppOpsManager,
uid,
- getCurIntent().getComponent().getPackageName())) {
+ getCurIntentLocked().getComponent().getPackageName())) {
return true;
}
return false;
@@ -3515,7 +3555,7 @@
@Override
public void showInputMethodPickerFromClient(IInputMethodClient client,
int auxiliarySubtypeMode) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledFromValidUserLocked()) {
return;
}
@@ -3552,14 +3592,14 @@
* A test API for CTS to make sure that the input method menu is showing.
*/
public boolean isInputMethodPickerShownForTest() {
- synchronized(mMethodMap) {
+ synchronized (ImfLock.class) {
return mMenuController.isisInputMethodPickerShownForTestLocked();
}
}
@BinderThread
private void setInputMethod(@NonNull IBinder token, String id) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -3570,7 +3610,7 @@
@BinderThread
private void setInputMethodAndSubtype(@NonNull IBinder token, String id,
InputMethodSubtype subtype) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -3587,19 +3627,19 @@
@Override
public void showInputMethodAndSubtypeEnablerFromClient(
IInputMethodClient client, String inputMethodId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
// TODO(yukawa): Should we verify the display ID?
if (!calledFromValidUserLocked()) {
return;
}
- executeOrSendMessage(getCurMethod(), mCaller.obtainMessageO(
+ executeOrSendMessage(getCurMethodLocked(), mCaller.obtainMessageO(
MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
}
}
@BinderThread
private boolean switchToPreviousInputMethod(@NonNull IBinder token) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return false;
}
@@ -3613,7 +3653,7 @@
String targetLastImiId = null;
int subtypeId = NOT_A_SUBTYPE_ID;
if (lastIme != null && lastImi != null) {
- final boolean imiIdIsSame = lastImi.getId().equals(getSelectedMethodId());
+ final boolean imiIdIsSame = lastImi.getId().equals(getSelectedMethodIdLocked());
final int lastSubtypeHash = Integer.parseInt(lastIme.second);
final int currentSubtypeHash = mCurrentSubtype == null ? NOT_A_SUBTYPE_ID
: mCurrentSubtype.hashCode();
@@ -3659,7 +3699,7 @@
if (!TextUtils.isEmpty(targetLastImiId)) {
if (DEBUG) {
Slog.d(TAG, "Switch to: " + lastImi.getId() + ", " + lastIme.second
- + ", from: " + getSelectedMethodId() + ", " + subtypeId);
+ + ", from: " + getSelectedMethodIdLocked() + ", " + subtypeId);
}
setInputMethodWithSubtypeIdLocked(token, targetLastImiId, subtypeId);
return true;
@@ -3671,12 +3711,12 @@
@BinderThread
private boolean switchToNextInputMethod(@NonNull IBinder token, boolean onlyCurrentIme) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return false;
}
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- onlyCurrentIme, mMethodMap.get(getSelectedMethodId()), mCurrentSubtype);
+ onlyCurrentIme, mMethodMap.get(getSelectedMethodIdLocked()), mCurrentSubtype);
if (nextSubtype == null) {
return false;
}
@@ -3688,12 +3728,12 @@
@BinderThread
private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return false;
}
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- false /* onlyCurrentIme */, mMethodMap.get(getSelectedMethodId()),
+ false /* onlyCurrentIme */, mMethodMap.get(getSelectedMethodIdLocked()),
mCurrentSubtype);
if (nextSubtype == null) {
return false;
@@ -3704,7 +3744,7 @@
@Override
public InputMethodSubtype getLastInputMethodSubtype() {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledFromValidUserLocked()) {
return null;
}
@@ -3742,7 +3782,7 @@
+ subtype.getLocale() + ", " + subtype.getMode());
}
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledFromValidUserLocked()) {
return;
}
@@ -3793,7 +3833,11 @@
@Override
public int getInputMethodWindowVisibleHeight() {
// TODO(yukawa): Should we verify the display ID?
- return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId);
+ final int curTokenDisplayId;
+ synchronized (ImfLock.class) {
+ curTokenDisplayId = mCurTokenDisplayId;
+ }
+ return mWindowManagerInternal.getInputMethodWindowVisibleHeight(curTokenDisplayId);
}
@Override
@@ -3872,7 +3916,7 @@
public void startImeTrace() {
ImeTracing.getInstance().startTrace(null /* printwriter */);
ArrayMap<IBinder, ClientState> clients;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
clients = new ArrayMap<>(mClients);
}
for (ClientState state : clients.values()) {
@@ -3891,7 +3935,7 @@
public void stopImeTrace() {
ImeTracing.getInstance().stopTrace(null /* printwriter */);
ArrayMap<IBinder, ClientState> clients;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
clients = new ArrayMap<>(mClients);
}
for (ClientState state : clients.values()) {
@@ -3906,10 +3950,10 @@
}
private void dumpDebug(ProtoOutputStream proto, long fieldId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final long token = proto.start(fieldId);
- proto.write(CUR_METHOD_ID, getSelectedMethodId());
- proto.write(CUR_SEQ, getSequenceNumber());
+ proto.write(CUR_METHOD_ID, getSelectedMethodIdLocked());
+ proto.write(CUR_SEQ, getSequenceNumberLocked());
proto.write(CUR_CLIENT, Objects.toString(mCurClient));
proto.write(CUR_FOCUSED_WINDOW_NAME,
mWindowManagerInternal.getWindowName(mCurFocusedWindow));
@@ -3920,17 +3964,17 @@
if (mCurAttribute != null) {
mCurAttribute.dumpDebug(proto, CUR_ATTRIBUTE);
}
- proto.write(CUR_ID, getCurId());
+ proto.write(CUR_ID, getCurIdLocked());
proto.write(SHOW_REQUESTED, mShowRequested);
proto.write(SHOW_EXPLICITLY_REQUESTED, mShowExplicitlyRequested);
proto.write(SHOW_FORCED, mShowForced);
proto.write(INPUT_SHOWN, mInputShown);
proto.write(IN_FULLSCREEN_MODE, mInFullscreenMode);
- proto.write(CUR_TOKEN, Objects.toString(getCurToken()));
+ proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked()));
proto.write(CUR_TOKEN_DISPLAY_ID, mCurTokenDisplayId);
proto.write(SYSTEM_READY, mSystemReady);
proto.write(LAST_SWITCH_USER_ID, mLastSwitchUserId);
- proto.write(HAVE_CONNECTION, hasConnection());
+ proto.write(HAVE_CONNECTION, hasConnectionLocked());
proto.write(BOUND_TO_METHOD, mBoundToMethod);
proto.write(IS_INTERACTIVE, mIsInteractive);
proto.write(BACK_DISPOSITION, mBackDisposition);
@@ -3947,15 +3991,15 @@
if (DEBUG) {
Slog.d(TAG, "Got the notification of a user action.");
}
- synchronized (mMethodMap) {
- if (getCurToken() != token) {
+ synchronized (ImfLock.class) {
+ if (getCurTokenLocked() != token) {
if (DEBUG) {
Slog.d(TAG, "Ignoring the user action notification from IMEs that are no longer"
+ " active.");
}
return;
}
- final InputMethodInfo imi = mMethodMap.get(getSelectedMethodId());
+ final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
if (imi != null) {
mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
}
@@ -3965,7 +4009,7 @@
@BinderThread
private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -3989,7 +4033,7 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
if (token == null) {
if (mContext.checkCallingOrSelfPermission(
@@ -3999,7 +4043,7 @@
"Using null token requires permission "
+ android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- } else if (getCurToken() != token) {
+ } else if (getCurTokenLocked() != token) {
Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
+ " token: " + token);
return;
@@ -4014,6 +4058,7 @@
}
/** Called right after {@link IInputMethod#showSoftInput}. */
+ @GuardedBy("ImfLock.class")
private void onShowHideSoftInputRequested(boolean show, IBinder requestToken,
@SoftInputShowHideReason int reason) {
final WindowManagerInternal.ImeTargetInfo info =
@@ -4028,7 +4073,7 @@
@BinderThread
private void hideMySoftInput(@NonNull IBinder token, int flags) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -4048,7 +4093,7 @@
@BinderThread
private void showMySoftInput(@NonNull IBinder token, int flags) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -4146,8 +4191,11 @@
final IBinder token = (IBinder) args.arg3;
((IInputMethod) args.arg1).showSoftInput(
token, msg.arg1 /* flags */, (ResultReceiver) args.arg2);
- final IBinder requestToken = mShowRequestWindowMap.get(token);
- onShowHideSoftInputRequested(true /* show */, requestToken, reason);
+ final IBinder requestToken;
+ synchronized (ImfLock.class) {
+ requestToken = mShowRequestWindowMap.get(token);
+ onShowHideSoftInputRequested(true /* show */, requestToken, reason);
+ }
} catch (RemoteException e) {
}
args.recycle();
@@ -4162,14 +4210,17 @@
final IBinder token = (IBinder) args.arg3;
((IInputMethod)args.arg1).hideSoftInput(
token, 0 /* flags */, (ResultReceiver) args.arg2);
- final IBinder requestToken = mHideRequestWindowMap.get(token);
- onShowHideSoftInputRequested(false /* show */, requestToken, reason);
+ final IBinder requestToken;
+ synchronized (ImfLock.class) {
+ requestToken = mHideRequestWindowMap.get(token);
+ onShowHideSoftInputRequested(false /* show */, requestToken, reason);
+ }
} catch (RemoteException e) {
}
args.recycle();
return true;
case MSG_HIDE_CURRENT_INPUT_METHOD:
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final @SoftInputShowHideReason int reason = (int) msg.obj;
hideCurrentInputLocked(mCurFocusedWindow, 0, null, reason);
@@ -4179,8 +4230,10 @@
args = (SomeArgs)msg.obj;
try {
if (DEBUG) {
- Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
- + mCurTokenDisplayId);
+ synchronized (ImfLock.class) {
+ Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
+ + mCurTokenDisplayId);
+ }
}
final IBinder token = (IBinder) args.arg2;
((IInputMethod) args.arg1).initializeInternal(token,
@@ -4207,7 +4260,7 @@
return true;
}
case MSG_REMOVE_IME_SURFACE: {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
try {
if (mEnabledSession != null && mEnabledSession.session != null
&& !mShowRequested) {
@@ -4220,7 +4273,7 @@
}
case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: {
IBinder windowToken = (IBinder) msg.obj;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
try {
if (windowToken == mCurFocusedWindow
&& mEnabledSession != null && mEnabledSession.session != null) {
@@ -4362,7 +4415,7 @@
}
private void handleSetInteractive(final boolean interactive) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
mIsInteractive = interactive;
updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);
@@ -4371,7 +4424,7 @@
boolean reportToImeController = false;
try {
reportToImeController = mPlatformCompat.isChangeEnabledByUid(
- FINISH_INPUT_NO_FALLBACK_CONNECTION, getCurMethodUid());
+ FINISH_INPUT_NO_FALLBACK_CONNECTION, getCurMethodUidLocked());
} catch (RemoteException e) {
}
scheduleSetActiveToClient(mCurClient, mIsInteractive, mInFullscreenMode,
@@ -4386,7 +4439,7 @@
active ? 1 : 0, fullscreen ? 1 : 0, reportToImeController ? 1 : 0, 0, state));
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean chooseNewDefaultIMELocked() {
final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
mSettings.getEnabledInputMethodListLocked());
@@ -4462,7 +4515,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
if (DEBUG) {
Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
@@ -4570,7 +4623,7 @@
mSettings.getCurrentUserId(), 0 /* unused */, inputMethodList).sendToTarget();
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void updateDefaultVoiceImeIfNeededLocked() {
final String systemSpeechRecognizer =
mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer);
@@ -4612,7 +4665,7 @@
intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId);
}
final int userId;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
userId = mSettings.getCurrentUserId();
}
mContext.startActivityAsUser(intent, null, UserHandle.of(userId));
@@ -4636,7 +4689,7 @@
* @param enabled {@code true} if {@code id} needs to be enabled.
* @return {@code true} if the IME was previously enabled. {@code false} otherwise.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
.getEnabledInputMethodsAndSubtypeListLocked();
@@ -4672,10 +4725,11 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
boolean setSubtypeOnly) {
- mSettings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodId(), mCurrentSubtype);
+ mSettings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(),
+ mCurrentSubtype);
// Set Subtype here
if (imi == null || subtypeId < 0) {
@@ -4699,7 +4753,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
InputMethodInfo imi = mMethodMap.get(newDefaultIme);
int lastSubtypeId = NOT_A_SUBTYPE_ID;
@@ -4723,7 +4777,7 @@
*/
@Override
public InputMethodSubtype getCurrentInputMethodSubtype() {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
// TODO: Make this work even for non-current users?
if (!calledFromValidUserLocked()) {
return null;
@@ -4732,9 +4786,9 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
- String selectedMethodId = getSelectedMethodId();
+ String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId == null) {
return null;
}
@@ -4773,19 +4827,14 @@
return mCurrentSubtype;
}
- @Nullable
- String getCurrentMethodId() {
- return getSelectedMethodId();
- }
-
private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
return getInputMethodListLocked(userId, DirectBootAwareness.AUTO);
}
}
private List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
return getEnabledInputMethodListLocked(userId);
}
}
@@ -4793,7 +4842,7 @@
private void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo,
IInlineSuggestionsRequestCallback callback) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
onCreateInlineSuggestionsRequestLocked(userId, requestInfo, callback);
}
}
@@ -4810,7 +4859,7 @@
}
private boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (userId == mSettings.getCurrentUserId()) {
if (!mMethodMap.containsKey(imeId)
|| !mSettings.getEnabledInputMethodListLocked()
@@ -4836,7 +4885,7 @@
}
private boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (userId == mSettings.getCurrentUserId()) {
if (!mMethodMap.containsKey(imeId)) {
return false; // IME is not found.
@@ -4868,7 +4917,7 @@
int displayId) {
//TODO(b/150843766): Check if Input Token is valid.
final IBinder curHostInputToken;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (displayId != mCurTokenDisplayId || mCurHostInputToken == null) {
return false;
}
@@ -4878,7 +4927,7 @@
}
private void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mCurFocusedWindow != windowToken) {
// mCurPerceptible was set by the focused window, but it is no longer in control,
// so we reset mCurPerceptible.
@@ -4986,13 +5035,13 @@
throw new InvalidParameterException("contentUri must have content scheme");
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int uid = Binder.getCallingUid();
- if (getSelectedMethodId() == null) {
+ if (getSelectedMethodIdLocked() == null) {
return null;
}
- if (getCurToken() != token) {
- Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + getCurToken()
+ if (getCurTokenLocked() != token) {
+ Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + getCurTokenLocked()
+ " token=" + token);
return null;
}
@@ -5025,7 +5074,7 @@
@BinderThread
private void reportFullscreenMode(@NonNull IBinder token, boolean fullscreen) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -5108,7 +5157,7 @@
final Printer p = new PrintWriterPrinter(pw);
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
p.println("Current Input Method Manager state:");
int N = mMethodList.size();
p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
@@ -5127,24 +5176,24 @@
p.println(" sessionRequested=" + ci.sessionRequested);
p.println(" curSession=" + ci.curSession);
}
- p.println(" mCurMethodId=" + getSelectedMethodId());
+ p.println(" mCurMethodId=" + getSelectedMethodIdLocked());
client = mCurClient;
- p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumber());
+ p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
p.println(" mCurPerceptible=" + mCurPerceptible);
p.println(" mCurFocusedWindow=" + mCurFocusedWindow
+ " softInputMode=" +
InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)
+ " client=" + mCurFocusedWindowClient);
focusedWindowClient = mCurFocusedWindowClient;
- p.println(" mCurId=" + getCurId() + " mHaveConnection=" + hasConnection()
+ p.println(" mCurId=" + getCurIdLocked() + " mHaveConnection=" + hasConnectionLocked()
+ " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
+ mBindingController.isVisibleBound());
- p.println(" mCurToken=" + getCurToken());
+ p.println(" mCurToken=" + getCurTokenLocked());
p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId);
p.println(" mCurHostInputToken=" + mCurHostInputToken);
- p.println(" mCurIntent=" + getCurIntent());
- method = getCurMethod();
- p.println(" mCurMethod=" + getCurMethod());
+ p.println(" mCurIntent=" + getCurIntentLocked());
+ method = getCurMethodLocked();
+ p.println(" mCurMethod=" + getCurMethodLocked());
p.println(" mEnabledSession=" + mEnabledSession);
p.println(" mShowRequested=" + mShowRequested
+ " mShowExplicitlyRequested=" + mShowExplicitlyRequested
@@ -5393,7 +5442,7 @@
@BinderThread
@ShellCommandResult
private int getLastSwitchUserId(@NonNull ShellCommand shellCommand) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
shellCommand.getOutPrintWriter().println(mLastSwitchUserId);
return ShellCommandResult.SUCCESS;
}
@@ -5428,7 +5477,7 @@
break;
}
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final PrintWriter pr = shellCommand.getOutPrintWriter();
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
@@ -5470,7 +5519,7 @@
final PrintWriter out = shellCommand.getOutPrintWriter();
final PrintWriter error = shellCommand.getErrPrintWriter();
boolean hasFailed = false;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
for (int userId : userIds) {
@@ -5524,7 +5573,7 @@
* @return {@code false} if it fails to enable the IME. {@code false} otherwise.
*/
@BinderThread
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean handleShellCommandEnableDisableInputMethodInternalLocked(
@UserIdInt int userId, String imeId, boolean enabled, PrintWriter out,
PrintWriter error) {
@@ -5594,7 +5643,7 @@
final PrintWriter out = shellCommand.getOutPrintWriter();
final PrintWriter error = shellCommand.getErrPrintWriter();
boolean hasFailed = false;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
for (int userId : userIds) {
@@ -5632,7 +5681,7 @@
private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
final PrintWriter out = shellCommand.getOutPrintWriter();
final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand);
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
for (int userId : userIds) {
@@ -5644,17 +5693,21 @@
if (userId == mSettings.getCurrentUserId()) {
hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
- mBindingController.unbindCurrentMethodLocked();
+ mBindingController.unbindCurrentMethod();
// Reset the current IME
resetSelectedInputMethodAndSubtypeLocked(null);
// Also reset the settings of the current IME
mSettings.putSelectedInputMethod(null);
// Disable all enabled IMEs.
- mSettings.getEnabledInputMethodListLocked().forEach(
- imi -> setInputMethodEnabledLocked(imi.getId(), false));
+ for (InputMethodInfo inputMethodInfo :
+ mSettings.getEnabledInputMethodListLocked()) {
+ setInputMethodEnabledLocked(inputMethodInfo.getId(), false);
+ }
// Re-enable with default enabled IMEs.
- InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList).forEach(
- imi -> setInputMethodEnabledLocked(imi.getId(), true));
+ for (InputMethodInfo imi :
+ InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList)) {
+ setInputMethodEnabledLocked(imi.getId(), true);
+ }
updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
mSettings.getEnabledInputMethodListLocked(),
@@ -5723,7 +5776,7 @@
}
boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
ArrayMap<IBinder, ClientState> clients;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
clients = new ArrayMap<>(mClients);
}
for (ClientState state : clients.values()) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index f70ad05..132be7d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -46,6 +46,7 @@
import android.widget.Switch;
import android.widget.TextView;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
@@ -98,7 +99,7 @@
int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final List<ImeSubtypeListItem> imList = mSwitchingController
.getSortedInputMethodAndSubtypeListForImeMenuLocked(
showAuxSubtypes, isScreenLocked);
@@ -112,7 +113,7 @@
final InputMethodSubtype currentSubtype =
mService.getCurrentInputMethodSubtypeLocked();
if (currentSubtype != null) {
- final String curMethodId = mService.getCurrentMethodId();
+ final String curMethodId = mService.getSelectedMethodIdLocked();
final InputMethodInfo currentImi = mMethodMap.get(curMethodId);
lastInputMethodSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
currentImi, currentSubtype.hashCode());
@@ -175,7 +176,7 @@
final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
final DialogInterface.OnClickListener choiceListener = (dialog, which) -> {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mIms == null || mIms.length <= which || mSubtypeIds == null
|| mSubtypeIds.length <= which) {
return;
@@ -250,11 +251,12 @@
}
void hideInputMethodMenu() {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
hideInputMethodMenuLocked();
}
}
+ @GuardedBy("ImfLock.class")
void hideInputMethodMenuLocked() {
if (DEBUG) Slog.v(TAG, "Hide switching menu");
@@ -299,7 +301,7 @@
if (DEBUG) {
Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
&& mSwitchingDialog.isShowing()) {
mSwitchingDialogTitleView.findViewById(
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index c340a2b..f8894c6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -39,7 +39,7 @@
* InputMethodSubtypeSwitchingController controls the switching behavior of the subtypes.
*
* <p>This class is designed to be used from and only from {@link InputMethodManagerService} by
- * using {@link InputMethodManagerService#mMethodMap} as a global lock.</p>
+ * using {@link ImfLock ImfLock.class} as a global lock.</p>
*/
final class InputMethodSubtypeSwitchingController {
private static final String TAG = InputMethodSubtypeSwitchingController.class.getSimpleName();
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 0fd7cc1..e40d86a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -331,15 +331,7 @@
mAppOpsManager = context.getSystemService(AppOpsManager.class);
startMonitoringOpChanges();
-
- HostEndpointInfo info = new HostEndpointInfo();
- info.hostEndpointId = (char) mHostEndPointId;
- info.packageName = mPackage;
- info.attributionTag = mAttributionTag;
- info.type = (mUid == Process.SYSTEM_UID)
- ? HostEndpointInfo.Type.TYPE_FRAMEWORK
- : HostEndpointInfo.Type.TYPE_APP;
- mContextHubProxy.onHostEndpointConnected(info);
+ sendHostEndpointConnectedEvent();
}
/* package */ ContextHubClientBroker(
@@ -556,6 +548,9 @@
/* package */ void onHubReset() {
invokeCallback(callback -> callback.onHubReset());
sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_HUB_RESET));
+
+ // Re-send the host endpoint connected event as the Context Hub restarted.
+ sendHostEndpointConnectedEvent();
}
/**
@@ -895,6 +890,17 @@
}
}
+ private void sendHostEndpointConnectedEvent() {
+ HostEndpointInfo info = new HostEndpointInfo();
+ info.hostEndpointId = (char) mHostEndPointId;
+ info.packageName = mPackage;
+ info.attributionTag = mAttributionTag;
+ info.type = (mUid == Process.SYSTEM_UID)
+ ? HostEndpointInfo.Type.TYPE_FRAMEWORK
+ : HostEndpointInfo.Type.TYPE_APP;
+ mContextHubProxy.onHostEndpointConnected(info);
+ }
+
/**
* Dump debugging info as ClientBrokerProto
*
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index 19f7c4d..c199bb3 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -152,6 +152,14 @@
@Override
/* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ ContextHubStatsLog.write(
+ ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED,
+ nanoAppBinary.getNanoAppId(),
+ nanoAppBinary.getNanoAppVersion(),
+ ContextHubStatsLog
+ .CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_TYPE__TYPE_LOAD,
+ toStatsTransactionResult(result));
+
if (result == ContextHubTransaction.RESULT_SUCCESS) {
// NOTE: The legacy JNI code used to do a query right after a load success
// to synchronize the service cache. Instead store the binary that was
@@ -200,6 +208,13 @@
@Override
/* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ ContextHubStatsLog.write(
+ ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED, nanoAppId,
+ 0 /* nanoappVersion */,
+ ContextHubStatsLog
+ .CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_TYPE__TYPE_UNLOAD,
+ toStatsTransactionResult(result));
+
if (result == ContextHubTransaction.RESULT_SUCCESS) {
mNanoAppStateManager.removeNanoAppInstance(contextHubId, nanoAppId);
}
@@ -477,6 +492,30 @@
}
}
+ private int toStatsTransactionResult(@ContextHubTransaction.Result int result) {
+ switch (result) {
+ case ContextHubTransaction.RESULT_SUCCESS:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_SUCCESS;
+ case ContextHubTransaction.RESULT_FAILED_BAD_PARAMS:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_BAD_PARAMS;
+ case ContextHubTransaction.RESULT_FAILED_UNINITIALIZED:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_UNINITIALIZED;
+ case ContextHubTransaction.RESULT_FAILED_BUSY:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_BUSY;
+ case ContextHubTransaction.RESULT_FAILED_AT_HUB:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_AT_HUB;
+ case ContextHubTransaction.RESULT_FAILED_TIMEOUT:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_TIMEOUT;
+ case ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_SERVICE_INTERNAL_FAILURE;
+ case ContextHubTransaction.RESULT_FAILED_HAL_UNAVAILABLE:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_HAL_UNAVAILABLE;
+ case ContextHubTransaction.RESULT_FAILED_UNKNOWN:
+ default: /* fall through */
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_UNKNOWN;
+ }
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(100);
diff --git a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index 47146c1..d08e5dc 100644
--- a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -21,26 +21,23 @@
import android.annotation.Nullable;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.reflect.Array;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.ConcurrentModificationException;
import java.util.NoSuchElementException;
import java.util.Objects;
/**
* An in-memory event log to support historical event information. The log is of a constant size,
* and new events will overwrite old events as the log fills up.
- *
- * @param <T> log event type
*/
public class LocalEventLog<T> {
- /**
- * Consumer of log events for iterating over the log.
- *
- * @param <T> log event type
- */
+ /** Consumer of log events for iterating over the log. */
public interface LogConsumer<T> {
/** Invoked with a time and a logEvent. */
void acceptLog(long time, T logEvent);
@@ -48,12 +45,13 @@
// masks for the entries field. 1 bit is used to indicate whether this is a filler event or not,
// and 31 bits to store the time delta.
- private static final int IS_FILLER_MASK = 0b10000000000000000000000000000000;
+ private static final int IS_FILLER_MASK = 0b10000000000000000000000000000000;
private static final int TIME_DELTA_MASK = 0b01111111111111111111111111111111;
private static final int IS_FILLER_OFFSET = countTrailingZeros(IS_FILLER_MASK);
private static final int TIME_DELTA_OFFSET = countTrailingZeros(TIME_DELTA_MASK);
+ @VisibleForTesting
static final int MAX_TIME_DELTA = (1 << bitCount(TIME_DELTA_MASK)) - 1;
private static int countTrailingZeros(int i) {
@@ -79,7 +77,7 @@
return (entry & IS_FILLER_MASK) != 0;
}
- // circular buffer of log entries and events. each entry corrosponds to the log event at the
+ // circular buffer of log entries and events. each entry corresponds to the log event at the
// same index. the log entry holds the filler status and time delta according to the bit masks
// above, and the log event is the log event.
@@ -103,6 +101,9 @@
@GuardedBy("this")
long mLastLogTime;
+ @GuardedBy("this")
+ long mModificationCount;
+
@SuppressWarnings("unchecked")
public LocalEventLog(int size, Class<T> clazz) {
Preconditions.checkArgument(size > 0);
@@ -143,6 +144,7 @@
if (isEmpty()) {
mStartTime = time;
mLastLogTime = mStartTime;
+ mModificationCount++;
}
addLogEventInternal(false, (int) delta, logEvent);
@@ -156,6 +158,7 @@
if (mLogSize == mEntries.length) {
// if log is full, size will remain the same, but update the start time
mStartTime += getTimeDelta(mEntries[startIndex()]);
+ mModificationCount++;
} else {
// otherwise add an item
mLogSize++;
@@ -170,11 +173,12 @@
/** Clears the log of all entries. */
public synchronized void clear() {
- // clear entries to allow gc
+ // clear entries to aid gc
Arrays.fill(mLogEvents, null);
mLogEndIndex = 0;
mLogSize = 0;
+ mModificationCount++;
mStartTime = -1;
mLastLogTime = -1;
@@ -186,7 +190,10 @@
return mLogSize == 0;
}
- /** Iterates over the event log, passing each log string to the given consumer. */
+ /**
+ * Iterates over the event log, passing each log event to the given consumer. Locks the log
+ * while executing so that {@link ConcurrentModificationException}s cannot occur.
+ */
public synchronized void iterate(LogConsumer<? super T> consumer) {
LogIterator it = new LogIterator();
while (it.hasNext()) {
@@ -195,15 +202,53 @@
}
}
+ /**
+ * Iterates over all the given event logs in time order, passing each log event to the given
+ * consumer. It is the caller's responsibility to ensure that {@link
+ * ConcurrentModificationException}s cannot occur, whether through locking or other means.
+ */
+ @SafeVarargs
+ public static <T> void iterate(LogConsumer<? super T> consumer, LocalEventLog<T>... logs) {
+ ArrayList<LocalEventLog<T>.LogIterator> its = new ArrayList<>(logs.length);
+ for (LocalEventLog<T> log : logs) {
+ LocalEventLog<T>.LogIterator it = log.new LogIterator();
+ if (it.hasNext()) {
+ its.add(it);
+ it.next();
+ }
+ }
+
+ while (true) {
+ LocalEventLog<T>.LogIterator next = null;
+ for (LocalEventLog<T>.LogIterator it : its) {
+ if (it != null && (next == null || it.getTime() < next.getTime())) {
+ next = it;
+ }
+ }
+
+ if (next == null) {
+ return;
+ }
+
+ consumer.acceptLog(next.getTime(), next.getLog());
+
+ if (next.hasNext()) {
+ next.next();
+ } else {
+ its.remove(next);
+ }
+ }
+ }
+
// returns the index of the first element
@GuardedBy("this")
- private int startIndex() {
+ int startIndex() {
return wrapIndex(mLogEndIndex - mLogSize);
}
// returns the index after this one
@GuardedBy("this")
- private int incrementIndex(int index) {
+ int incrementIndex(int index) {
if (index == -1) {
return startIndex();
} else if (index >= 0) {
@@ -215,12 +260,15 @@
// rolls over the given index if necessary
@GuardedBy("this")
- private int wrapIndex(int index) {
+ int wrapIndex(int index) {
// java modulo will keep negative sign, we need to rollover
return (index % mEntries.length + mEntries.length) % mEntries.length;
}
- private class LogIterator {
+ /** Iterator over log times and events. */
+ protected final class LogIterator {
+
+ private final long mModificationCount;
private long mLogTime;
private int mIndex;
@@ -229,8 +277,10 @@
private long mCurrentTime;
private T mCurrentLogEvent;
- LogIterator() {
+ public LogIterator() {
synchronized (LocalEventLog.this) {
+ mModificationCount = LocalEventLog.this.mModificationCount;
+
mLogTime = mStartTime;
mIndex = -1;
mCount = -1;
@@ -241,6 +291,7 @@
public boolean hasNext() {
synchronized (LocalEventLog.this) {
+ checkModifications();
return mCount < mLogSize;
}
}
@@ -277,5 +328,12 @@
}
} while (mCount < mLogSize && isFiller(mEntries[mIndex]));
}
+
+ @GuardedBy("LocalEventLog.this")
+ private void checkModifications() {
+ if (mModificationCount != LocalEventLog.this.mModificationCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index 94953e0..45436e7 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -52,16 +52,28 @@
if (D) {
return 600;
} else {
+ return 300;
+ }
+ }
+
+ private static int getLocationsLogSize() {
+ if (D) {
return 200;
+ } else {
+ return 100;
}
}
@GuardedBy("mAggregateStats")
private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
- public LocationEventLog() {
+ @GuardedBy("this")
+ private final LocationsEventLog mLocationsLog;
+
+ private LocationEventLog() {
super(getLogSize(), Object.class);
mAggregateStats = new ArrayMap<>(4);
+ mLocationsLog = new LocationsEventLog(getLocationsLogSize());
}
/** Copies out all aggregated stats. */
@@ -95,39 +107,39 @@
/** Logs a user switched event. */
public void logUserSwitched(int userIdFrom, int userIdTo) {
- addLogEvent(new UserSwitchedEvent(userIdFrom, userIdTo));
+ addLog(new UserSwitchedEvent(userIdFrom, userIdTo));
}
/** Logs a location enabled/disabled event. */
public void logLocationEnabled(int userId, boolean enabled) {
- addLogEvent(new LocationEnabledEvent(userId, enabled));
+ addLog(new LocationEnabledEvent(userId, enabled));
}
/** Logs a location enabled/disabled event. */
public void logAdasLocationEnabled(int userId, boolean enabled) {
- addLogEvent(new LocationAdasEnabledEvent(userId, enabled));
+ addLog(new LocationAdasEnabledEvent(userId, enabled));
}
/** Logs a location provider enabled/disabled event. */
public void logProviderEnabled(String provider, int userId, boolean enabled) {
- addLogEvent(new ProviderEnabledEvent(provider, userId, enabled));
+ addLog(new ProviderEnabledEvent(provider, userId, enabled));
}
/** Logs a location provider being replaced/unreplaced by a mock provider. */
public void logProviderMocked(String provider, boolean mocked) {
- addLogEvent(new ProviderMockedEvent(provider, mocked));
+ addLog(new ProviderMockedEvent(provider, mocked));
}
/** Logs a new client registration for a location provider. */
public void logProviderClientRegistered(String provider, CallerIdentity identity,
LocationRequest request) {
- addLogEvent(new ProviderClientRegisterEvent(provider, true, identity, request));
+ addLog(new ProviderClientRegisterEvent(provider, true, identity, request));
getAggregateStats(provider, identity).markRequestAdded(request.getIntervalMillis());
}
/** Logs a client unregistration for a location provider. */
public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
- addLogEvent(new ProviderClientRegisterEvent(provider, false, identity, null));
+ addLog(new ProviderClientRegisterEvent(provider, false, identity, null));
getAggregateStats(provider, identity).markRequestRemoved();
}
@@ -144,7 +156,7 @@
/** Logs a client for a location provider entering the foreground state. */
public void logProviderClientForeground(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(new ProviderClientForegroundEvent(provider, true, identity));
+ addLog(new ProviderClientForegroundEvent(provider, true, identity));
}
getAggregateStats(provider, identity).markRequestForeground();
}
@@ -152,7 +164,7 @@
/** Logs a client for a location provider leaving the foreground state. */
public void logProviderClientBackground(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(new ProviderClientForegroundEvent(provider, false, identity));
+ addLog(new ProviderClientForegroundEvent(provider, false, identity));
}
getAggregateStats(provider, identity).markRequestBackground();
}
@@ -160,32 +172,34 @@
/** Logs a client for a location provider entering the permitted state. */
public void logProviderClientPermitted(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(new ProviderClientPermittedEvent(provider, true, identity));
+ addLog(new ProviderClientPermittedEvent(provider, true, identity));
}
}
/** Logs a client for a location provider leaving the permitted state. */
public void logProviderClientUnpermitted(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(new ProviderClientPermittedEvent(provider, false, identity));
+ addLog(new ProviderClientPermittedEvent(provider, false, identity));
}
}
/** Logs a change to the provider request for a location provider. */
public void logProviderUpdateRequest(String provider, ProviderRequest request) {
- addLogEvent(new ProviderUpdateEvent(provider, request));
+ addLog(new ProviderUpdateEvent(provider, request));
}
/** Logs a new incoming location for a location provider. */
public void logProviderReceivedLocations(String provider, int numLocations) {
- addLogEvent(new ProviderReceiveLocationEvent(provider, numLocations));
+ synchronized (this) {
+ mLocationsLog.logProviderReceivedLocations(provider, numLocations);
+ }
}
/** Logs a location deliver for a client of a location provider. */
public void logProviderDeliveredLocations(String provider, int numLocations,
CallerIdentity identity) {
- if (D) {
- addLogEvent(new ProviderDeliverLocationEvent(provider, numLocations, identity));
+ synchronized (this) {
+ mLocationsLog.logProviderDeliveredLocations(provider, numLocations, identity);
}
getAggregateStats(provider, identity).markLocationDelivered();
}
@@ -193,19 +207,24 @@
/** Logs that a provider has entered or exited stationary throttling. */
public void logProviderStationaryThrottled(String provider, boolean throttled,
ProviderRequest request) {
- addLogEvent(new ProviderStationaryThrottledEvent(provider, throttled, request));
+ addLog(new ProviderStationaryThrottledEvent(provider, throttled, request));
}
/** Logs that the location power save mode has changed. */
public void logLocationPowerSaveMode(
@LocationPowerSaveMode int locationPowerSaveMode) {
- addLogEvent(new LocationPowerSaveModeEvent(locationPowerSaveMode));
+ addLog(new LocationPowerSaveModeEvent(locationPowerSaveMode));
}
- private void addLogEvent(Object logEvent) {
+ private void addLog(Object logEvent) {
addLog(SystemClock.elapsedRealtime(), logEvent);
}
+ @Override
+ public synchronized void iterate(LogConsumer<? super Object> consumer) {
+ iterate(consumer, this, mLocationsLog);
+ }
+
public void iterate(Consumer<String> consumer) {
iterate(consumer, null);
}
@@ -488,6 +507,26 @@
}
}
+ private static final class LocationsEventLog extends LocalEventLog<Object> {
+
+ LocationsEventLog(int size) {
+ super(size, Object.class);
+ }
+
+ public void logProviderReceivedLocations(String provider, int numLocations) {
+ addLog(new ProviderReceiveLocationEvent(provider, numLocations));
+ }
+
+ public void logProviderDeliveredLocations(String provider, int numLocations,
+ CallerIdentity identity) {
+ addLog(new ProviderDeliverLocationEvent(provider, numLocations, identity));
+ }
+
+ private void addLog(Object logEvent) {
+ this.addLog(SystemClock.elapsedRealtime(), logEvent);
+ }
+ }
+
/**
* Aggregate statistics for a single package under a single provider.
*/
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 699f143..7bb0d48 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -114,7 +114,8 @@
protected boolean registerWithService(GnssMeasurementRequest request,
Collection<GnssListenerRegistration> registrations) {
if (mGnssNative.startMeasurementCollection(request.isFullTracking(),
- request.isCorrelationVectorOutputsEnabled())) {
+ request.isCorrelationVectorOutputsEnabled(),
+ request.getIntervalMillis())) {
if (D) {
Log.d(TAG, "starting gnss measurements (" + request + ")");
}
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index 1eef0de..a513e08 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -623,8 +623,38 @@
public void injectLocation(Location location) {
Preconditions.checkState(mRegistered);
if (location.hasAccuracy()) {
- mGnssHal.injectLocation(location.getLatitude(), location.getLongitude(),
- location.getAccuracy());
+
+ int gnssLocationFlags = GNSS_LOCATION_HAS_LAT_LONG
+ | (location.hasAltitude() ? GNSS_LOCATION_HAS_ALTITUDE : 0)
+ | (location.hasSpeed() ? GNSS_LOCATION_HAS_SPEED : 0)
+ | (location.hasBearing() ? GNSS_LOCATION_HAS_BEARING : 0)
+ | (location.hasAccuracy() ? GNSS_LOCATION_HAS_HORIZONTAL_ACCURACY : 0)
+ | (location.hasVerticalAccuracy() ? GNSS_LOCATION_HAS_VERTICAL_ACCURACY : 0)
+ | (location.hasSpeedAccuracy() ? GNSS_LOCATION_HAS_SPEED_ACCURACY : 0)
+ | (location.hasBearingAccuracy() ? GNSS_LOCATION_HAS_BEARING_ACCURACY : 0);
+
+ double latitudeDegrees = location.getLatitude();
+ double longitudeDegrees = location.getLongitude();
+ double altitudeMeters = location.getAltitude();
+ float speedMetersPerSec = location.getSpeed();
+ float bearingDegrees = location.getBearing();
+ float horizontalAccuracyMeters = location.getAccuracy();
+ float verticalAccuracyMeters = location.getVerticalAccuracyMeters();
+ float speedAccuracyMetersPerSecond = location.getSpeedAccuracyMetersPerSecond();
+ float bearingAccuracyDegrees = location.getBearingAccuracyDegrees();
+ long timestamp = location.getTime();
+
+ int elapsedRealtimeFlags = GNSS_REALTIME_HAS_TIMESTAMP_NS
+ | (location.hasElapsedRealtimeUncertaintyNanos()
+ ? GNSS_REALTIME_HAS_TIME_UNCERTAINTY_NS : 0);
+ long elapsedRealtimeNanos = location.getElapsedRealtimeNanos();
+ double elapsedRealtimeUncertaintyNanos = location.getElapsedRealtimeUncertaintyNanos();
+
+ mGnssHal.injectLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+ altitudeMeters, speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
+ verticalAccuracyMeters, speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
+ timestamp, elapsedRealtimeFlags, elapsedRealtimeNanos,
+ elapsedRealtimeUncertaintyNanos);
}
}
@@ -735,9 +765,10 @@
* Starts measurement collection.
*/
public boolean startMeasurementCollection(boolean enableFullTracking,
- boolean enableCorrVecOutputs) {
+ boolean enableCorrVecOutputs, int intervalMillis) {
Preconditions.checkState(mRegistered);
- return mGnssHal.startMeasurementCollection(enableFullTracking, enableCorrVecOutputs);
+ return mGnssHal.startMeasurementCollection(enableFullTracking, enableCorrVecOutputs,
+ intervalMillis);
}
/**
@@ -1262,8 +1293,15 @@
return native_read_nmea(buffer, bufferSize);
}
- protected void injectLocation(double latitude, double longitude, float accuracy) {
- native_inject_location(latitude, longitude, accuracy);
+ protected void injectLocation(@GnssLocationFlags int gnssLocationFlags, double latitude,
+ double longitude, double altitude, float speed, float bearing,
+ float horizontalAccuracy, float verticalAccuracy, float speedAccuracy,
+ float bearingAccuracy, long timestamp, @GnssRealtimeFlags int elapsedRealtimeFlags,
+ long elapsedRealtimeNanos, double elapsedRealtimeUncertaintyNanos) {
+ native_inject_location(gnssLocationFlags, latitude, longitude, altitude, speed,
+ bearing, horizontalAccuracy, verticalAccuracy, speedAccuracy, bearingAccuracy,
+ timestamp, elapsedRealtimeFlags, elapsedRealtimeNanos,
+ elapsedRealtimeUncertaintyNanos);
}
protected void injectBestLocation(@GnssLocationFlags int gnssLocationFlags, double latitude,
@@ -1310,8 +1348,9 @@
}
protected boolean startMeasurementCollection(boolean enableFullTracking,
- boolean enableCorrVecOutputs) {
- return native_start_measurement_collection(enableFullTracking, enableCorrVecOutputs);
+ boolean enableCorrVecOutputs, int intervalMillis) {
+ return native_start_measurement_collection(enableFullTracking, enableCorrVecOutputs,
+ intervalMillis);
}
protected boolean stopMeasurementCollection() {
@@ -1436,8 +1475,13 @@
// location injection APIs
- private static native void native_inject_location(double latitude, double longitude,
- float accuracy);
+ private static native void native_inject_location(
+ int gnssLocationFlags, double latitudeDegrees, double longitudeDegrees,
+ double altitudeMeters, float speedMetersPerSec, float bearingDegrees,
+ float horizontalAccuracyMeters, float verticalAccuracyMeters,
+ float speedAccuracyMetersPerSecond, float bearingAccuracyDegrees,
+ long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos,
+ double elapsedRealtimeUncertaintyNanos);
private static native void native_inject_best_location(
@@ -1475,7 +1519,7 @@
private static native boolean native_is_measurement_supported();
private static native boolean native_start_measurement_collection(boolean enableFullTracking,
- boolean enableCorrVecOutputs);
+ boolean enableCorrVecOutputs, int intervalMillis);
private static native boolean native_stop_measurement_collection();
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 771bf19..1ba32ac 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -245,38 +245,19 @@
intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
}
- PendingIntent.OnFinished onFinished = null;
-
- // send() SHOULD only run the completion callback if it completes successfully. however,
- // b/201299281 (which could not be fixed in the S timeframe) means that it's possible
- // for send() to throw an exception AND run the completion callback. if this happens, we
- // would over-release the wakelock... we take matters into our own hands to ensure that
- // the completion callback can only be run if send() completes successfully. this means
- // the completion callback may be run inline - but as we've never specified what thread
- // the callback is run on, this is fine.
- GatedCallback gatedCallback;
+ Runnable callback = null;
if (onCompleteCallback != null) {
- gatedCallback = new GatedCallback(() -> {
+ callback = () -> {
try {
onCompleteCallback.sendResult(null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- });
- onFinished = (pI, i, rC, rD, rE) -> gatedCallback.run();
- } else {
- gatedCallback = new GatedCallback(null);
+ };
}
- mPendingIntent.send(
- mContext,
- 0,
- intent,
- onFinished,
- null,
- null,
+ PendingIntentSender.send(mPendingIntent, mContext, intent, callback,
options.toBundle());
- gatedCallback.allow();
}
@Override
@@ -1783,12 +1764,26 @@
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
CancellationSignal.fromTransport(cancelTransport)
- .setOnCancelListener(SingleUseCallback.wrap(
+ .setOnCancelListener(
() -> {
- synchronized (mLock) {
- removeRegistration(callback.asBinder(), registration);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ removeRegistration(callback.asBinder(), registration);
+ }
+ } catch (RuntimeException e) {
+ // since this is within a oneway binder transaction there is nowhere
+ // for exceptions to go - move onto another thread to crash system
+ // server so we find out about it
+ FgThread.getExecutor().execute(() -> {
+ throw new AssertionError(e);
+ });
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- }));
+
+ });
return cancelTransport;
}
@@ -2733,103 +2728,84 @@
}
}
- private static class SingleUseCallback extends IRemoteCallback.Stub implements Runnable,
- CancellationSignal.OnCancelListener {
+ private static class PendingIntentSender {
- public static @Nullable SingleUseCallback wrap(@Nullable Runnable callback) {
- return callback == null ? null : new SingleUseCallback(callback);
- }
-
- @GuardedBy("this")
- private @Nullable Runnable mCallback;
-
- private SingleUseCallback(Runnable callback) {
- mCallback = Objects.requireNonNull(callback);
- }
-
- @Override
- public void sendResult(Bundle data) {
- run();
- }
-
- @Override
- public void onCancel() {
- run();
- }
-
- @Override
- public void run() {
- Runnable callback;
- synchronized (this) {
- callback = mCallback;
- mCallback = null;
+ // send() SHOULD only run the OnFinished callback if it completes successfully. however,
+ // b/201299281 (which could not be fixed in the S timeframe) means that it's possible
+ // for send() to throw an exception AND run the completion callback which breaks the
+ // guarantee we rely on. we take matters into our own hands to ensure that the OnFinished
+ // callback can only be run if send() completes successfully. this means the OnFinished
+ // callback may be run inline, so there is no longer any guarantee about what thread the
+ // callback will be run on.
+ public static void send(PendingIntent pendingIntent, Context context, Intent intent,
+ @Nullable final Runnable callback, Bundle options)
+ throws PendingIntent.CanceledException {
+ GatedCallback gatedCallback;
+ PendingIntent.OnFinished onFinished;
+ if (callback != null) {
+ gatedCallback = new GatedCallback(callback);
+ onFinished = (pI, i, rC, rD, rE) -> gatedCallback.run();
+ } else {
+ gatedCallback = null;
+ onFinished = null;
}
- // prevent this callback from being run more than once - otherwise this could provide an
- // attack vector for a malicious app to break assumptions on how many times a callback
- // may be invoked, and thus crash system server.
- if (callback == null) {
- return;
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- callback.run();
- } catch (RuntimeException e) {
- // since this is within a oneway binder transaction there is nowhere
- // for exceptions to go - move onto another thread to crash system
- // server so we find out about it
- FgThread.getExecutor().execute(() -> {
- throw new AssertionError(e);
- });
- throw e;
- } finally {
- Binder.restoreCallingIdentity(identity);
+ pendingIntent.send(
+ context,
+ 0,
+ intent,
+ onFinished,
+ null,
+ null,
+ options);
+ if (gatedCallback != null) {
+ gatedCallback.allow();
}
}
- }
- private static class GatedCallback implements Runnable {
+ private static class GatedCallback implements Runnable {
- private @Nullable Runnable mCallback;
+ @GuardedBy("this")
+ private @Nullable Runnable mCallback;
- @GuardedBy("this")
- private boolean mGate;
- @GuardedBy("this")
- private boolean mRun;
+ @GuardedBy("this")
+ private boolean mGate;
+ @GuardedBy("this")
+ private boolean mRun;
- GatedCallback(@Nullable Runnable callback) {
- mCallback = callback;
- }
+ private GatedCallback(@Nullable Runnable callback) {
+ mCallback = callback;
+ }
- public void allow() {
- Runnable callback = null;
- synchronized (this) {
- mGate = true;
- if (mRun && mCallback != null) {
- callback = mCallback;
- mCallback = null;
+ public void allow() {
+ Runnable callback = null;
+ synchronized (this) {
+ mGate = true;
+ if (mRun && mCallback != null) {
+ callback = mCallback;
+ mCallback = null;
+ }
+ }
+
+ if (callback != null) {
+ callback.run();
}
}
- if (callback != null) {
- callback.run();
- }
- }
-
- @Override
- public void run() {
- Runnable callback = null;
- synchronized (this) {
- mRun = true;
- if (mGate && mCallback != null) {
- callback = mCallback;
- mCallback = null;
+ @Override
+ public void run() {
+ Runnable callback = null;
+ synchronized (this) {
+ mRun = true;
+ if (mGate && mCallback != null) {
+ callback = mCallback;
+ mCallback = null;
+ }
}
- }
- if (callback != null) {
- callback.run();
+ if (callback != null) {
+ callback.run();
+ }
}
}
}
@@ -2850,7 +2826,19 @@
try {
mWakeLock.release();
} catch (RuntimeException e) {
- Log.e(TAG, "wakelock over-released by " + mIdentity, e);
+ // wakelock throws a RuntimeException instead of some more specific exception, so
+ // attempt to capture only actual RuntimeExceptions
+ if (e.getClass() == RuntimeException.class) {
+ Log.e(TAG, "wakelock over-released by " + mIdentity, e);
+ } else {
+ // since this is within a oneway binder transaction there is nowhere for
+ // exceptions to go - move onto another thread to crash system server so we find
+ // out about it
+ FgThread.getExecutor().execute(() -> {
+ throw new AssertionError(e);
+ });
+ throw e;
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index bedb8b9..22cd06d 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -101,6 +101,12 @@
mPm.mSettings.writeKernelMappingLPr(ps);
}
+ // TODO(b/211761016): should we still create the profile dirs?
+ if (!shouldHaveAppStorage(pkg)) {
+ Slog.w(TAG, "Skipping preparing app data for " + pkg.getPackageName());
+ return;
+ }
+
Installer.Batch batch = new Installer.Batch();
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
StorageManagerInternal smInternal = mInjector.getLocalService(
@@ -161,6 +167,10 @@
Slog.wtf(TAG, "Package was null!", new Throwable());
return CompletableFuture.completedFuture(null);
}
+ if (!shouldHaveAppStorage(pkg)) {
+ Slog.w(TAG, "Skipping preparing app data for " + pkg.getPackageName());
+ return CompletableFuture.completedFuture(null);
+ }
return prepareAppDataLeaf(batch, pkg, previousAppId, userId, flags);
}
@@ -381,7 +391,7 @@
for (File file : files) {
final String packageName = file.getName();
try {
- assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
+ assertPackageStorageValid(volumeUuid, packageName, userId);
} catch (PackageManagerException e) {
logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
try {
@@ -398,7 +408,7 @@
for (File file : files) {
final String packageName = file.getName();
try {
- assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
+ assertPackageStorageValid(volumeUuid, packageName, userId);
} catch (PackageManagerException e) {
logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
try {
@@ -446,7 +456,11 @@
return result;
}
- private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
+ /**
+ * Asserts that storage path is valid by checking that {@code packageName} is present,
+ * installed for the given {@code userId} and can have app data.
+ */
+ private void assertPackageStorageValid(String volumeUuid, String packageName, int userId)
throws PackageManagerException {
synchronized (mPm.mLock) {
// Normalize package name to handle renamed packages
@@ -462,6 +476,13 @@
} else if (!ps.getInstalled(userId)) {
throw new PackageManagerException(
"Package " + packageName + " not installed for user " + userId);
+ } else if (ps.getPkg() == null) {
+ throw new PackageManagerException("Package " + packageName + " is not parsed yet");
+ } else {
+ if (!shouldHaveAppStorage(ps.getPkg())) {
+ throw new PackageManagerException(
+ "Package " + packageName + " shouldn't have storage");
+ }
}
}
}
@@ -603,4 +624,13 @@
Slog.w(TAG, String.valueOf(e));
}
}
+
+ /**
+ * Returns {@code true} if app's internal storage should be created for this {@code pkg}.
+ */
+ private boolean shouldHaveAppStorage(AndroidPackage pkg) {
+ PackageManager.Property noAppDataProp =
+ pkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
+ return noAppDataProp == null || !noAppDataProp.getBoolean();
+ }
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index c2e4145..2f4f271 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1560,7 +1560,7 @@
: mPermissionManager.getGrantedPermissions(ps.getPackageName(), userId);
PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
- ps.getFirstInstallTime(), ps.getLastUpdateTime(), permissions, state, userId,
+ state.getFirstInstallTime(), ps.getLastUpdateTime(), permissions, state, userId,
ps);
if (packageInfo == null) {
@@ -1577,7 +1577,7 @@
pi.packageName = ps.getPackageName();
pi.setLongVersionCode(ps.getVersionCode());
pi.sharedUserId = (ps.getSharedUser() != null) ? ps.getSharedUser().name : null;
- pi.firstInstallTime = ps.getFirstInstallTime();
+ pi.firstInstallTime = state.getFirstInstallTime();
pi.lastUpdateTime = ps.getLastUpdateTime();
ApplicationInfo ai = new ApplicationInfo();
@@ -5436,12 +5436,11 @@
}
PackageDexUsage.PackageUseInfo packageUseInfo =
mDexManager.getPackageUseInfoOrDefault(packageState.getPackageName());
- if (PackageManagerServiceUtils
- .isUnusedSinceTimeInMillis(packageState.getFirstInstallTime(),
- currentTimeInMillis, downgradeTimeThresholdMillis, packageUseInfo,
- packageState.getTransientState().getLatestPackageUseTimeInMills(),
- packageState.getTransientState()
- .getLatestForegroundPackageUseTimeInMills())) {
+ if (PackageManagerServiceUtils.isUnusedSinceTimeInMillis(
+ PackageStateUtils.getEarliestFirstInstallTime(packageState.getUserStates()),
+ currentTimeInMillis, downgradeTimeThresholdMillis, packageUseInfo,
+ packageState.getTransientState().getLatestPackageUseTimeInMills(),
+ packageState.getTransientState().getLatestForegroundPackageUseTimeInMills())) {
unusedPackages.add(packageState.getPackageName());
}
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index c26f3b3..9a80a4e 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -556,7 +556,8 @@
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
- null /*splashScreenTheme*/);
+ null /*splashScreenTheme*/,
+ 0 /*firstInstallTime*/);
}
mPm.mSettings.writeKernelMappingLPr(ps);
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index c595b97..27b6282 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -561,6 +561,7 @@
pkgSetting.setHidden(false, userId);
pkgSetting.setInstallReason(installReason, userId);
pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId);
+ pkgSetting.setFirstInstallTime(System.currentTimeMillis(), userId);
mPm.mSettings.writePackageRestrictionsLPr(userId);
mPm.mSettings.writeKernelMappingLPr(pkgSetting);
installed = true;
@@ -941,17 +942,26 @@
if (result.needsNewAppId()) {
request.mInstallResult.mRemovedInfo.mAppIdChanging = true;
}
+ if (!checkNoAppStorageIsConsistent(
+ result.mRequest.mOldPkg, result.mPkgSetting.getPkg())) {
+ // TODO: INSTALL_FAILED_UPDATE_INCOMPATIBLE is about incomptabible
+ // signatures. Is there a better error code?
+ request.mInstallResult.setError(
+ INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "Update attempted to change value of "
+ + PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
+ return;
+ }
createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
- if (result.mStaticSharedLibraryInfo != null
- || result.mSdkSharedLibraryInfo != null) {
- final PackageSetting sharedLibLatestVersionSetting =
- mSharedLibraries.getSharedLibLatestVersionSetting(result);
- if (sharedLibLatestVersionSetting != null) {
+ if (result.mStaticSharedLibraryInfo != null) {
+ final PackageSetting staticSharedLibLatestVersionSetting =
+ mSharedLibraries.getStaticSharedLibLatestVersionSetting(result);
+ if (staticSharedLibLatestVersionSetting != null) {
lastStaticSharedLibSettings.put(
result.mPkgSetting.getPkg().getPackageName(),
- sharedLibLatestVersionSetting);
+ staticSharedLibLatestVersionSetting);
}
}
} catch (PackageManagerException e) {
@@ -1039,6 +1049,22 @@
}
@GuardedBy("mPm.mInstallLock")
+ private boolean checkNoAppStorageIsConsistent(AndroidPackage oldPkg, AndroidPackage newPkg) {
+ if (oldPkg == null) {
+ // New install, nothing to check against.
+ return true;
+ }
+ final PackageManager.Property curProp =
+ oldPkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
+ final PackageManager.Property newProp =
+ newPkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
+ if (curProp == null || !curProp.getBoolean()) {
+ return newProp == null || !newProp.getBoolean();
+ }
+ return newProp != null && newProp.getBoolean();
+ }
+
+ @GuardedBy("mPm.mInstallLock")
private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
throws PrepareFailure {
final int installFlags = args.mInstallFlags;
@@ -1902,7 +1928,7 @@
PackageStateInternal deletedPkgSetting = mPm.getPackageStateInternal(
oldPackage.getPackageName());
reconciledPkg.mPkgSetting
- .setFirstInstallTime(deletedPkgSetting.getFirstInstallTime())
+ .setFirstInstallTimeFromReplaced(deletedPkgSetting, request.mAllUsers)
.setLastUpdateTime(System.currentTimeMillis());
res.mRemovedInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
@@ -3558,22 +3584,20 @@
boolean appIdCreated = false;
try {
final String pkgName = scanResult.mPkgSetting.getPackageName();
+ final ReconcileRequest reconcileRequest = new ReconcileRequest(
+ Collections.singletonMap(pkgName, scanResult),
+ mSharedLibraries.getAll(), mPm.mPackages,
+ Collections.singletonMap(pkgName,
+ mPm.getSettingsVersionForPackage(parsedPackage)),
+ Collections.singletonMap(pkgName,
+ mSharedLibraries.getStaticSharedLibLatestVersionSetting(
+ scanResult)));
final Map<String, ReconciledPackage> reconcileResult =
- ReconcilePackageUtils.reconcilePackages(
- new ReconcileRequest(
- Collections.singletonMap(pkgName, scanResult),
- mSharedLibraries.getAll(),
- mPm.mPackages,
- Collections.singletonMap(
- pkgName,
- mPm.getSettingsVersionForPackage(
- parsedPackage)),
- Collections.singletonMap(pkgName, mSharedLibraries
- .getSharedLibLatestVersionSetting(scanResult))),
+ ReconcilePackageUtils.reconcilePackages(reconcileRequest,
mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
appIdCreated = optimisticallyRegisterAppId(scanResult);
- commitReconciledScanResultLocked(
- reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
+ commitReconciledScanResultLocked(reconcileResult.get(pkgName),
+ mPm.mUserManager.getUserIds());
} catch (PackageManagerException e) {
if (appIdCreated) {
cleanUpAppIdCreation(scanResult);
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index f7d4dba..1de239e 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -59,6 +59,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
@@ -854,7 +855,10 @@
} else if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() <
rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
return -1;
- } else if (lhsPs.getFirstInstallTime() > rhsPs.getFirstInstallTime()) {
+ } else if (
+ PackageStateUtils.getEarliestFirstInstallTime(lhsPs.getUserStates())
+ > PackageStateUtils.getEarliestFirstInstallTime(
+ rhsPs.getUserStates())) {
return 1;
} else {
return -1;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 0777cde..ca87685 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -88,6 +88,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -103,6 +104,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.ExecutionException;
/**
* Service that manages requests and callbacks for launchers that support
@@ -728,9 +730,16 @@
return null;
}
- final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
- getCallingUserId(), callingPackage, packageName, shortcutId,
- user.getIdentifier(), injectBinderCallingPid(), injectBinderCallingUid());
+ final AndroidFuture<Intent[]> ret = new AndroidFuture<>();
+ Intent[] intents;
+ mShortcutServiceInternal.createShortcutIntentsAsync(getCallingUserId(),
+ callingPackage, packageName, shortcutId, user.getIdentifier(),
+ injectBinderCallingPid(), injectBinderCallingUid(), ret);
+ try {
+ intents = ret.get();
+ } catch (InterruptedException | ExecutionException e) {
+ return null;
+ }
if (intents == null || intents.length == 0) {
return null;
}
@@ -901,6 +910,40 @@
}
@Override
+ public void getShortcutsAsync(@NonNull final String callingPackage,
+ @NonNull final ShortcutQueryWrapper query, @NonNull final UserHandle targetUser,
+ @NonNull final AndroidFuture<List<ShortcutInfo>> cb) {
+ ensureShortcutPermission(callingPackage);
+ if (!canAccessProfile(targetUser.getIdentifier(), "Cannot get shortcuts")) {
+ cb.complete(Collections.EMPTY_LIST);
+ return;
+ }
+
+ final long changedSince = query.getChangedSince();
+ final String packageName = query.getPackage();
+ final List<String> shortcutIds = query.getShortcutIds();
+ final List<LocusId> locusIds = query.getLocusIds();
+ final ComponentName componentName = query.getActivity();
+ final int flags = query.getQueryFlags();
+ if (shortcutIds != null && packageName == null) {
+ throw new IllegalArgumentException(
+ "To query by shortcut ID, package name must also be set");
+ }
+ if (locusIds != null && packageName == null) {
+ throw new IllegalArgumentException(
+ "To query by locus ID, package name must also be set");
+ }
+ if ((query.getQueryFlags() & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
+ ensureStrictAccessShortcutsPermission(callingPackage);
+ }
+
+ mShortcutServiceInternal.getShortcutsAsync(getCallingUserId(),
+ callingPackage, changedSince, packageName, shortcutIds, locusIds,
+ componentName, flags, targetUser.getIdentifier(),
+ injectBinderCallingPid(), injectBinderCallingUid(), cb);
+ }
+
+ @Override
public void registerShortcutChangeCallback(@NonNull final String callingPackage,
@NonNull final ShortcutQueryWrapper query,
@NonNull final IShortcutChangeCallback callback) {
@@ -991,8 +1034,14 @@
return null;
}
- return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
- callingPackage, packageName, id, targetUserId);
+ final AndroidFuture<ParcelFileDescriptor> ret = new AndroidFuture<>();
+ mShortcutServiceInternal.getShortcutIconFdAsync(getCallingUserId(),
+ callingPackage, packageName, id, targetUserId, ret);
+ try {
+ return ret.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
@@ -1003,8 +1052,14 @@
return null;
}
- return mShortcutServiceInternal.getShortcutIconUri(getCallingUserId(), callingPackage,
- packageName, shortcutId, userId);
+ final AndroidFuture<String> ret = new AndroidFuture<>();
+ mShortcutServiceInternal.getShortcutIconUriAsync(getCallingUserId(), callingPackage,
+ packageName, shortcutId, userId, ret);
+ try {
+ return ret.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
@@ -1037,9 +1092,16 @@
ensureShortcutPermission(callerUid, callerPid, callingPackage);
}
- final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
- callingUserId, callingPackage, packageName, shortcutId, targetUserId,
- callerPid, callerUid);
+ final AndroidFuture<Intent[]> ret = new AndroidFuture<>();
+ Intent[] intents;
+ mShortcutServiceInternal.createShortcutIntentsAsync(getCallingUserId(), callingPackage,
+ packageName, shortcutId, targetUserId,
+ injectBinderCallingPid(), injectBinderCallingUid(), ret);
+ try {
+ intents = ret.get();
+ } catch (InterruptedException | ExecutionException e) {
+ return false;
+ }
if (intents == null || intents.length == 0) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f474044..cfcf199 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -404,7 +404,11 @@
if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) {
// Aggressively close old sessions because we are running low on storage
// Their staging dirs will be removed too
- session.abandon();
+ PackageInstallerSession root = !session.hasParentSessionId()
+ ? session : mSessions.get(session.getParentSessionId());
+ if (!root.isDestroyed()) {
+ root.abandon();
+ }
} else {
// Session is new enough, so it deserves to be kept even on low storage
unclaimedStagingDirsOnVolume.remove(session.stageDir);
@@ -1623,7 +1627,7 @@
progress);
}
- public void onStagedSessionChanged(PackageInstallerSession session) {
+ public void onSessionChanged(PackageInstallerSession session) {
session.markUpdated();
mSettingsWriteRequest.schedule();
if (mOkToSendBroadcasts && !session.isDestroyed()) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a94985c..f45e54b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -81,7 +81,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -151,7 +151,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
-import com.android.server.SystemConfig;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -229,8 +228,8 @@
private static final String ATTR_IS_READY = "isReady";
private static final String ATTR_IS_FAILED = "isFailed";
private static final String ATTR_IS_APPLIED = "isApplied";
- private static final String ATTR_STAGED_SESSION_ERROR_CODE = "errorCode";
- private static final String ATTR_STAGED_SESSION_ERROR_MESSAGE = "errorMessage";
+ private static final String ATTR_SESSION_ERROR_CODE = "errorCode";
+ private static final String ATTR_SESSION_ERROR_MESSAGE = "errorMessage";
private static final String ATTR_MODE = "mode";
private static final String ATTR_INSTALL_FLAGS = "installFlags";
private static final String ATTR_INSTALL_LOCATION = "installLocation";
@@ -454,22 +453,22 @@
@GuardedBy("mLock")
private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private boolean mSessionApplied;
+ @GuardedBy("mLock")
+ private boolean mSessionReady;
+ @GuardedBy("mLock")
+ private boolean mSessionFailed;
+ @GuardedBy("mLock")
+ private int mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
+ @GuardedBy("mLock")
+ private String mSessionErrorMessage;
+
@Nullable
final StagedSession mStagedSession;
@VisibleForTesting
public class StagedSession implements StagingManager.StagedSession {
- @GuardedBy("mLock")
- private boolean mSessionApplied;
- @GuardedBy("mLock")
- private boolean mSessionReady;
- @GuardedBy("mLock")
- private boolean mSessionFailed;
- @GuardedBy("mLock")
- private int mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
- @GuardedBy("mLock")
- private String mSessionErrorMessage;
-
/**
* The callback to run when pre-reboot verification has ended. Used by {@link #abandon()}
* to delay session clean-up until it is safe to do so.
@@ -478,15 +477,6 @@
@Nullable
private Runnable mPendingAbandonCallback;
- StagedSession(boolean isReady, boolean isApplied, boolean isFailed, int errorCode,
- String errorMessage) {
- mSessionReady = isReady;
- mSessionApplied = isApplied;
- mSessionFailed = isFailed;
- mSessionErrorCode = errorCode;
- mSessionErrorMessage = errorMessage != null ? errorMessage : "";
- }
-
@Override
public List<StagingManager.StagedSession> getChildSessions() {
if (!params.isMultiPackage) {
@@ -534,52 +524,17 @@
@Override
public void setSessionReady() {
- synchronized (mLock) {
- // Do not allow destroyed/failed staged session to change state
- if (mDestroyed || mSessionFailed) return;
- mSessionReady = true;
- mSessionApplied = false;
- mSessionFailed = false;
- mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
- mSessionErrorMessage = "";
- }
- mCallback.onStagedSessionChanged(PackageInstallerSession.this);
+ PackageInstallerSession.this.setSessionReady();
}
@Override
public void setSessionFailed(int errorCode, String errorMessage) {
- List<PackageInstallerSession> childSessions;
- synchronized (mLock) {
- // Do not allow destroyed/failed staged session to change state
- if (mDestroyed || mSessionFailed) return;
- mSessionReady = false;
- mSessionApplied = false;
- mSessionFailed = true;
- mSessionErrorCode = errorCode;
- mSessionErrorMessage = errorMessage;
- Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
- childSessions = getChildSessionsLocked();
- }
- destroy();
- mCallback.onStagedSessionChanged(PackageInstallerSession.this);
+ PackageInstallerSession.this.setSessionFailed(errorCode, errorMessage);
}
@Override
public void setSessionApplied() {
- List<PackageInstallerSession> childSessions;
- synchronized (mLock) {
- // Do not allow destroyed/failed staged session to change state
- if (mDestroyed || mSessionFailed) return;
- mSessionReady = false;
- mSessionApplied = true;
- mSessionFailed = false;
- mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
- mSessionErrorMessage = "";
- Slog.d(TAG, "Marking session " + sessionId + " as applied");
- childSessions = getChildSessionsLocked();
- }
- destroy();
- mCallback.onStagedSessionChanged(PackageInstallerSession.this);
+ PackageInstallerSession.this.setSessionApplied();
}
@Override
@@ -656,35 +611,17 @@
@Override
public boolean isSessionReady() {
- synchronized (mLock) {
- return mSessionReady;
- }
+ return PackageInstallerSession.this.isSessionReady();
}
@Override
public boolean isSessionApplied() {
- synchronized (mLock) {
- return mSessionApplied;
- }
+ return PackageInstallerSession.this.isSessionApplied();
}
@Override
public boolean isSessionFailed() {
- synchronized (mLock) {
- return mSessionFailed;
- }
- }
-
- @StagedSessionErrorCode int getSessionErrorCode() {
- synchronized (mLock) {
- return mSessionErrorCode;
- }
- }
-
- String getSessionErrorMessage() {
- synchronized (mLock) {
- return mSessionErrorMessage;
- }
+ return PackageInstallerSession.this.isSessionFailed();
}
@Override
@@ -714,7 +651,7 @@
if (mStageDirInUse) {
// Pre-reboot verification is ongoing, not safe to clean up the session yet.
mPendingAbandonCallback = r;
- mCallback.onStagedSessionChanged(PackageInstallerSession.this);
+ mCallback.onSessionChanged(PackageInstallerSession.this);
return;
}
}
@@ -1015,8 +952,8 @@
ArrayMap<String, PerFileChecksum> checksums,
boolean prepared, boolean committed, boolean destroyed, boolean sealed,
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
- boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
- String stagedSessionErrorMessage) {
+ boolean isFailed, boolean isApplied, int sessionErrorCode,
+ String sessionErrorMessage) {
mCallback = callback;
mContext = context;
mPm = pm;
@@ -1071,8 +1008,13 @@
mPrepared = prepared;
mCommitted.set(committed);
mDestroyed = destroyed;
- mStagedSession = params.isStaged ? new StagedSession(isReady, isApplied, isFailed,
- stagedSessionErrorCode, stagedSessionErrorMessage) : null;
+ mSessionReady = isReady;
+ mSessionApplied = isApplied;
+ mSessionFailed = isFailed;
+ mSessionErrorCode = sessionErrorCode;
+ mSessionErrorMessage =
+ sessionErrorMessage != null ? sessionErrorMessage : "";
+ mStagedSession = params.isStaged ? new StagedSession() : null;
if (isDataLoaderInstallation()) {
if (isApexSession()) {
@@ -1173,11 +1115,10 @@
info.rollbackDataPolicy = params.rollbackDataPolicy;
info.parentSessionId = mParentSessionId;
info.childSessionIds = getChildSessionIdsLocked();
- info.isStagedSessionApplied = isStagedSessionApplied();
- info.isStagedSessionReady = isStagedSessionReady();
- info.isStagedSessionFailed = isStagedSessionFailed();
- info.setStagedSessionErrorCode(getStagedSessionErrorCode(),
- getStagedSessionErrorMessage());
+ info.isSessionApplied = mSessionApplied;
+ info.isSessionReady = mSessionReady;
+ info.isSessionFailed = mSessionFailed;
+ info.setSessionErrorCode(mSessionErrorCode, mSessionErrorMessage);
info.createdMillis = createdMillis;
info.updatedMillis = updatedMillis;
info.requireUserAction = params.requireUserAction;
@@ -2229,7 +2170,7 @@
final PackageInstallerSession root = hasParentSessionId()
? allSessions.get(getParentSessionId())
: this;
- if (root != null) {
+ if (root != null && !root.isStagedAndInTerminalState()) {
if (isApexSession()) {
validateApexInstallLocked();
} else {
@@ -2357,28 +2298,6 @@
return;
}
-
- // Check if APEX update is allowed. We do this check in handleInstall, since this is one of
- // the places that:
- // * Shared between staged and non-staged APEX update flows.
- // * Only is called after boot completes.
- // The later is important, since isApexUpdateAllowed check depends on the
- // ModuleInfoProvider, which is only populated after device has booted.
- if (isApexSession()) {
- boolean checkApexUpdateAllowed =
- (params.installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK)
- == 0;
- synchronized (mLock) {
- if (checkApexUpdateAllowed && !isApexUpdateAllowed(mPackageName,
- mInstallSource.installerPackageName)) {
- onSessionValidationFailure(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
- "Update of APEX package " + mPackageName + " is not allowed for "
- + mInstallSource.installerPackageName);
- return;
- }
- }
- }
-
if (params.isStaged) {
// TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
// though ideally, we just need to send session committed broadcast.
@@ -2825,25 +2744,6 @@
return sessionContains((s) -> !s.isApexSession());
}
- private boolean isApexUpdateAllowed(String apexPackageName, String installerPackageName) {
- if (mPm.getModuleInfo(apexPackageName, 0) != null) {
- final String modulesInstaller =
- SystemConfig.getInstance().getModulesInstallerPackageName();
- if (modulesInstaller == null) {
- Slog.w(TAG, "No modules installer defined");
- return false;
- }
- return modulesInstaller.equals(installerPackageName);
- }
- final String vendorApexInstaller =
- SystemConfig.getInstance().getAllowedVendorApexes().get(apexPackageName);
- if (vendorApexInstaller == null) {
- Slog.w(TAG, apexPackageName + " is not allowed to be updated");
- return false;
- }
- return vendorApexInstaller.equals(installerPackageName);
- }
-
/**
* Validate apex install.
* <p>
@@ -4261,30 +4161,83 @@
}
}
- /** {@hide} */
- boolean isStagedSessionReady() {
- return params.isStaged && mStagedSession.isSessionReady();
+ private void setSessionReady() {
+ synchronized (mLock) {
+ // Do not allow destroyed/failed session to change state
+ if (mDestroyed || mSessionFailed) return;
+ mSessionReady = true;
+ mSessionApplied = false;
+ mSessionFailed = false;
+ mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
+ mSessionErrorMessage = "";
+ }
+ mCallback.onSessionChanged(this);
+ }
+
+ private void setSessionFailed(int errorCode, String errorMessage) {
+ synchronized (mLock) {
+ // Do not allow destroyed/failed session to change state
+ if (mDestroyed || mSessionFailed) return;
+ mSessionReady = false;
+ mSessionApplied = false;
+ mSessionFailed = true;
+ mSessionErrorCode = errorCode;
+ mSessionErrorMessage = errorMessage;
+ Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
+ }
+ destroy();
+ mCallback.onSessionChanged(this);
+ }
+
+ private void setSessionApplied() {
+ synchronized (mLock) {
+ // Do not allow destroyed/failed session to change state
+ if (mDestroyed || mSessionFailed) return;
+ mSessionReady = false;
+ mSessionApplied = true;
+ mSessionFailed = false;
+ mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
+ mSessionErrorMessage = "";
+ Slog.d(TAG, "Marking session " + sessionId + " as applied");
+ }
+ destroy();
+ mCallback.onSessionChanged(this);
}
/** {@hide} */
- boolean isStagedSessionApplied() {
- return params.isStaged && mStagedSession.isSessionApplied();
+ boolean isSessionReady() {
+ synchronized (mLock) {
+ return mSessionReady;
+ }
}
/** {@hide} */
- boolean isStagedSessionFailed() {
- return params.isStaged && mStagedSession.isSessionFailed();
+ boolean isSessionApplied() {
+ synchronized (mLock) {
+ return mSessionApplied;
+ }
}
/** {@hide} */
- @StagedSessionErrorCode int getStagedSessionErrorCode() {
- return params.isStaged ? mStagedSession.getSessionErrorCode()
- : SessionInfo.STAGED_SESSION_NO_ERROR;
+ boolean isSessionFailed() {
+ synchronized (mLock) {
+ return mSessionFailed;
+ }
}
/** {@hide} */
- String getStagedSessionErrorMessage() {
- return params.isStaged ? mStagedSession.getSessionErrorMessage() : "";
+ @SessionErrorCode
+ int getSessionErrorCode() {
+ synchronized (mLock) {
+ return mSessionErrorCode;
+ }
+ }
+
+ /** {@hide} */
+ String getSessionErrorMessage() {
+ synchronized (mLock) {
+ return mSessionErrorMessage;
+ }
}
/**
@@ -4386,11 +4339,11 @@
pw.printPair("params.isStaged", params.isStaged);
pw.printPair("mParentSessionId", mParentSessionId);
pw.printPair("mChildSessionIds", getChildSessionIdsLocked());
- pw.printPair("mStagedSessionApplied", isStagedSessionApplied());
- pw.printPair("mStagedSessionFailed", isStagedSessionFailed());
- pw.printPair("mStagedSessionReady", isStagedSessionReady());
- pw.printPair("mStagedSessionErrorCode", getStagedSessionErrorCode());
- pw.printPair("mStagedSessionErrorMessage", getStagedSessionErrorMessage());
+ pw.printPair("mSessionApplied", mSessionApplied);
+ pw.printPair("mSessionFailed", mSessionFailed);
+ pw.printPair("mSessionReady", mSessionReady);
+ pw.printPair("mSessionErrorCode", mSessionErrorCode);
+ pw.printPair("mSessionErrorMessage", mSessionErrorMessage);
pw.println();
pw.decreaseIndent();
@@ -4556,12 +4509,11 @@
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
- writeBooleanAttribute(out, ATTR_IS_READY, isStagedSessionReady());
- writeBooleanAttribute(out, ATTR_IS_FAILED, isStagedSessionFailed());
- writeBooleanAttribute(out, ATTR_IS_APPLIED, isStagedSessionApplied());
- out.attributeInt(null, ATTR_STAGED_SESSION_ERROR_CODE, getStagedSessionErrorCode());
- writeStringAttribute(out, ATTR_STAGED_SESSION_ERROR_MESSAGE,
- getStagedSessionErrorMessage());
+ writeBooleanAttribute(out, ATTR_IS_READY, mSessionReady);
+ writeBooleanAttribute(out, ATTR_IS_FAILED, mSessionFailed);
+ writeBooleanAttribute(out, ATTR_IS_APPLIED, mSessionApplied);
+ out.attributeInt(null, ATTR_SESSION_ERROR_CODE, mSessionErrorCode);
+ writeStringAttribute(out, ATTR_SESSION_ERROR_MESSAGE, mSessionErrorMessage);
// TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
// we've read all sessions.
out.attributeInt(null, ATTR_PARENT_SESSION_ID, mParentSessionId);
@@ -4752,10 +4704,9 @@
final boolean isReady = in.getAttributeBoolean(null, ATTR_IS_READY, false);
final boolean isFailed = in.getAttributeBoolean(null, ATTR_IS_FAILED, false);
final boolean isApplied = in.getAttributeBoolean(null, ATTR_IS_APPLIED, false);
- final int stagedSessionErrorCode = in.getAttributeInt(null, ATTR_STAGED_SESSION_ERROR_CODE,
+ final int sessionErrorCode = in.getAttributeInt(null, ATTR_SESSION_ERROR_CODE,
SessionInfo.STAGED_SESSION_NO_ERROR);
- final String stagedSessionErrorMessage = readStringAttribute(in,
- ATTR_STAGED_SESSION_ERROR_MESSAGE);
+ final String sessionErrorMessage = readStringAttribute(in, ATTR_SESSION_ERROR_MESSAGE);
if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
throw new IllegalArgumentException("Can't restore staged session with invalid state.");
@@ -4869,6 +4820,6 @@
installerUid, installSource, params, createdMillis, committedMillis, stageDir,
stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed,
childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
- stagedSessionErrorCode, stagedSessionErrorMessage);
+ sessionErrorCode, sessionErrorMessage);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 4f21d0e..a532fe3 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -45,6 +45,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageHelper;
import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.rollback.RollbackManagerInternal;
@@ -99,9 +100,11 @@
storeSession(session.mStagedSession);
if (session.isMultiPackage()) {
for (PackageInstallerSession child : session.getChildSessions()) {
+ checkApexUpdateAllowed(child);
checkRebootlessApex(child);
}
} else {
+ checkApexUpdateAllowed(session);
checkRebootlessApex(session);
}
verifyAPK(session, callback);
@@ -203,7 +206,7 @@
}
private void onVerificationFailure(StagingManager.StagedSession session, Callback callback,
- @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) {
+ @SessionInfo.SessionErrorCode int errorCode, String errorMessage) {
if (!ensureActiveApexSessionIsAborted(session)) {
Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
// Safe to ignore active apex session abortion failure since session will be marked
@@ -461,6 +464,51 @@
return mApexManager.abortStagedSession(sessionId);
}
+ private boolean isApexUpdateAllowed(String apexPackageName, String installerPackageName) {
+ if (mPm.getModuleInfo(apexPackageName, 0) != null) {
+ final String modulesInstaller =
+ SystemConfig.getInstance().getModulesInstallerPackageName();
+ if (modulesInstaller == null) {
+ Slog.w(TAG, "No modules installer defined");
+ return false;
+ }
+ return modulesInstaller.equals(installerPackageName);
+ }
+ final String vendorApexInstaller =
+ SystemConfig.getInstance().getAllowedVendorApexes().get(apexPackageName);
+ if (vendorApexInstaller == null) {
+ Slog.w(TAG, apexPackageName + " is not allowed to be updated");
+ return false;
+ }
+ return vendorApexInstaller.equals(installerPackageName);
+ }
+
+ /**
+ * Checks if APEX update is allowed.
+ *
+ * This phase is shared between staged and non-staged sessions and should be called after
+ * boot is completed since this check depends on the ModuleInfoProvider, which is only populated
+ * after device has booted.
+ */
+ private void checkApexUpdateAllowed(PackageInstallerSession session)
+ throws PackageManagerException {
+ if (!session.isApexSession()) {
+ return;
+ }
+ final int installFlags = session.params.installFlags;
+ if ((installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK) != 0) {
+ return;
+ }
+ final String packageName = session.getPackageName();
+ final String installerPackageName = session.getInstallSource().installerPackageName;
+ if (!isApexUpdateAllowed(packageName, installerPackageName)) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Update of APEX package " + packageName + " is not allowed for "
+ + installerPackageName);
+ }
+ }
+
/**
* Fails this rebootless APEX session if the same package name found in any staged sessions.
*/
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 0da57bc..9dbf57d 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -33,6 +33,7 @@
import android.content.pm.UserInfo;
import android.content.pm.overlay.OverlayPaths;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.service.pm.PackageProto;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -157,7 +158,6 @@
private String mCpuAbiOverride;
private long mLastModifiedTime;
- private long firstInstallTime;
private long lastUpdateTime;
private long versionCode;
@@ -284,7 +284,6 @@
proto.write(PackageProto.NAME, (mRealName != null ? mRealName : mName));
proto.write(PackageProto.UID, mAppId);
proto.write(PackageProto.VERSION_CODE, versionCode);
- proto.write(PackageProto.INSTALL_TIME_MS, firstInstallTime);
proto.write(PackageProto.UPDATE_TIME_MS, lastUpdateTime);
proto.write(PackageProto.INSTALLER_NAME, installSource.installerPackageName);
@@ -335,8 +334,36 @@
return this;
}
- public PackageSetting setFirstInstallTime(long firstInstallTime) {
- this.firstInstallTime = firstInstallTime;
+ /**
+ * In case of replacing an old package, restore the first install timestamps if it was installed
+ * for the same users
+ */
+ public PackageSetting setFirstInstallTimeFromReplaced(PackageStateInternal replacedPkgSetting,
+ int[] userIds) {
+ for (int userId = 0; userId < userIds.length; userId++) {
+ final long previousFirstInstallTime =
+ replacedPkgSetting.getUserStateOrDefault(userId).getFirstInstallTime();
+ if (previousFirstInstallTime != 0) {
+ modifyUserState(userId).setFirstInstallTime(previousFirstInstallTime);
+ }
+ }
+ onChanged();
+ return this;
+ }
+
+ /**
+ * Set the time for the first time when an app is installed for a user. If userId specifies all
+ * users, set the same timestamp for all the users.
+ */
+ public PackageSetting setFirstInstallTime(long firstInstallTime, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ int userStateCount = mUserStates.size();
+ for (int i = 0; i < userStateCount; i++) {
+ mUserStates.valueAt(i).setFirstInstallTime(firstInstallTime);
+ }
+ } else {
+ modifyUserState(userId).setFirstInstallTime(firstInstallTime);
+ }
onChanged();
return this;
}
@@ -612,7 +639,6 @@
mSecondaryCpuAbi = other.mSecondaryCpuAbi;
mCpuAbiOverride = other.mCpuAbiOverride;
mLastModifiedTime = other.mLastModifiedTime;
- firstInstallTime = other.firstInstallTime;
lastUpdateTime = other.lastUpdateTime;
versionCode = other.versionCode;
signatures = other.signatures;
@@ -899,7 +925,8 @@
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int installReason, int uninstallReason,
- String harmfulAppWarning, String splashScreenTheme) {
+ String harmfulAppWarning, String splashScreenTheme,
+ long firstInstallTime) {
modifyUserState(userId)
.setSuspendParams(suspendParams)
.setCeDataInode(ceDataInode)
@@ -917,7 +944,8 @@
.setInstantApp(instantApp)
.setVirtualPreload(virtualPreload)
.setHarmfulAppWarning(harmfulAppWarning)
- .setSplashScreenTheme(splashScreenTheme);
+ .setSplashScreenTheme(splashScreenTheme)
+ .setFirstInstallTime(firstInstallTime);
onChanged();
}
@@ -930,7 +958,8 @@
new ArraySet<>(otherState.getEnabledComponentsNoCopy()),
new ArraySet<>(otherState.getDisabledComponentsNoCopy()),
otherState.getInstallReason(), otherState.getUninstallReason(),
- otherState.getHarmfulAppWarning(), otherState.getSplashScreenTheme());
+ otherState.getHarmfulAppWarning(), otherState.getSplashScreenTheme(),
+ otherState.getFirstInstallTime());
}
ArraySet<String> getEnabledComponents(int userId) {
@@ -1121,6 +1150,8 @@
proto.write(
PackageProto.UserInfoProto.LAST_DISABLED_APP_CALLER,
state.getLastDisableAppCaller());
+ proto.write(PackageProto.UserInfoProto.FIRST_INSTALL_TIME_MS,
+ state.getFirstInstallTime());
proto.end(userToken);
}
}
@@ -1486,11 +1517,6 @@
}
@DataClass.Generated.Member
- public long getFirstInstallTime() {
- return firstInstallTime;
- }
-
- @DataClass.Generated.Member
public long getLastUpdateTime() {
return lastUpdateTime;
}
@@ -1555,10 +1581,10 @@
}
@DataClass.Generated(
- time = 1636145878985L,
+ time = 1640923794772L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n boolean removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 378c9e0..6d2ec0d 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -84,6 +84,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.PackageStateUtils;
import dalvik.system.VMRuntime;
@@ -410,16 +411,19 @@
// Take care of first install / last update times.
final long scanFileTime = getLastModifiedTime(parsedPackage);
+ final long existingFirstInstallTime = userId == UserHandle.USER_ALL
+ ? PackageStateUtils.getEarliestFirstInstallTime(pkgSetting.getUserStates())
+ : pkgSetting.readUserState(userId).getFirstInstallTime();
if (currentTime != 0) {
- if (pkgSetting.getFirstInstallTime() == 0) {
- pkgSetting.setFirstInstallTime(currentTime)
+ if (existingFirstInstallTime == 0) {
+ pkgSetting.setFirstInstallTime(currentTime, userId)
.setLastUpdateTime(currentTime);
} else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
pkgSetting.setLastUpdateTime(currentTime);
}
- } else if (pkgSetting.getFirstInstallTime() == 0) {
- // We need *something*. Take time time stamp of the file.
- pkgSetting.setFirstInstallTime(scanFileTime)
+ } else if (existingFirstInstallTime == 0) {
+ // We need *something*. Take time stamp of the file.
+ pkgSetting.setFirstInstallTime(scanFileTime, userId)
.setLastUpdateTime(scanFileTime);
} else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
if (scanFileTime != pkgSetting.getLastModifiedTime()) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6c47eb0..21df5a9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -355,6 +355,7 @@
private static final String ATTR_SDK_VERSION = "sdkVersion";
private static final String ATTR_DATABASE_VERSION = "databaseVersion";
private static final String ATTR_VALUE = "value";
+ private static final String ATTR_FIRST_INSTALL_TIME = "first-install-time";
private final PackageManagerTracedLock mLock;
@@ -1007,8 +1008,9 @@
null /*disabledComponents*/,
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
- null, /*harmfulAppWarning*/
- null /*splashscreenTheme*/
+ null /*harmfulAppWarning*/,
+ null /*splashscreenTheme*/,
+ 0 /*firstInstallTime*/
);
}
}
@@ -1611,7 +1613,8 @@
}
}
- void readPackageRestrictionsLPr(int userId) {
+ void readPackageRestrictionsLPr(int userId,
+ @NonNull ArrayMap<String, Long> origFirstInstallTimes) {
if (DEBUG_MU) {
Log.i(TAG, "Reading package restrictions for user=" + userId);
}
@@ -1664,7 +1667,9 @@
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
- null /* splashScreenTheme*/);
+ null /* splashScreenTheme*/,
+ 0 /*firstInstallTime*/
+ );
}
return;
}
@@ -1754,6 +1759,8 @@
PackageManager.UNINSTALL_REASON_UNKNOWN);
final String splashScreenTheme = parser.getAttributeValue(null,
ATTR_SPLASH_SCREEN_THEME);
+ final long firstInstallTime = parser.getAttributeLongHex(null,
+ ATTR_FIRST_INSTALL_TIME, 0);
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
@@ -1826,7 +1833,9 @@
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
hidden, distractionFlags, suspendParamsMap, instantApp, virtualPreload,
enabledCaller, enabledComponents, disabledComponents, installReason,
- uninstallReason, harmfulAppWarning, splashScreenTheme);
+ uninstallReason, harmfulAppWarning, splashScreenTheme,
+ firstInstallTime != 0 ? firstInstallTime :
+ origFirstInstallTimes.getOrDefault(name, 0L));
mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
} else if (tagName.equals("preferred-activities")) {
@@ -2079,6 +2088,8 @@
serializer.attributeInt(null, ATTR_INSTALL_REASON,
ustate.getInstallReason());
}
+ serializer.attributeLongHex(null, ATTR_FIRST_INSTALL_TIME,
+ ustate.getFirstInstallTime());
if (ustate.getUninstallReason() != PackageManager.UNINSTALL_REASON_UNKNOWN) {
serializer.attributeInt(null, ATTR_UNINSTALL_REASON,
ustate.getUninstallReason());
@@ -2746,7 +2757,6 @@
}
serializer.attribute(null, "codePath", pkg.getPathString());
serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
- serializer.attributeLongHex(null, "it", pkg.getFirstInstallTime());
serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
serializer.attributeLong(null, "version", pkg.getVersionCode());
if (pkg.getLegacyNativeLibraryPath() != null) {
@@ -2803,7 +2813,6 @@
serializer.attributeInt(null, "publicFlags", pkg.getFlags());
serializer.attributeInt(null, "privateFlags", pkg.getPrivateFlags());
serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
- serializer.attributeLongHex(null, "it", pkg.getFirstInstallTime());
serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
serializer.attributeLong(null, "version", pkg.getVersionCode());
if (pkg.getSharedUser() == null) {
@@ -2925,6 +2934,11 @@
mKeySetRefs.clear();
mInstallerPackages.clear();
+ // If any user state doesn't have a first install time, e.g., after an OTA,
+ // use the pre OTA firstInstallTime timestamp. This is because we migrated from per package
+ // firstInstallTime to per user-state. Without this, OTA can cause this info to be lost.
+ final ArrayMap<String, Long> originalFirstInstallTimes = new ArrayMap<>();
+
try {
if (str == null) {
if (!mSettingsFilename.exists()) {
@@ -2965,7 +2979,7 @@
String tagName = parser.getName();
if (tagName.equals("package")) {
- readPackageLPw(parser, users);
+ readPackageLPw(parser, users, originalFirstInstallTimes);
} else if (tagName.equals("permissions")) {
mPermissions.readPermissions(parser);
} else if (tagName.equals("permission-trees")) {
@@ -3102,7 +3116,7 @@
writePackageRestrictionsLPr(UserHandle.USER_SYSTEM);
} else {
for (UserInfo user : users) {
- readPackageRestrictionsLPr(user.id);
+ readPackageRestrictionsLPr(user.id, originalFirstInstallTimes);
}
}
@@ -3532,7 +3546,6 @@
timeStamp = parser.getAttributeLong(null, "ts", 0);
}
ps.setLastModifiedTime(timeStamp);
- ps.setFirstInstallTime(parser.getAttributeLongHex(null, "it", 0));
ps.setLastUpdateTime(parser.getAttributeLongHex(null, "ut", 0));
ps.setAppId(parser.getAttributeInt(null, "userId", 0));
if (ps.getAppId() <= 0) {
@@ -3570,7 +3583,8 @@
private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
- private void readPackageLPw(TypedXmlPullParser parser, List<UserInfo> users)
+ private void readPackageLPw(TypedXmlPullParser parser, List<UserInfo> users,
+ ArrayMap<String, Long> originalFirstInstallTimes)
throws XmlPullParserException, IOException {
String name = null;
String realName = null;
@@ -3726,7 +3740,6 @@
+ parser.getPositionDescription());
} else {
packageSetting.setLastModifiedTime(timeStamp);
- packageSetting.setFirstInstallTime(firstInstallTime);
packageSetting.setLastUpdateTime(lastUpdateTime);
}
} else if (sharedUserId != 0) {
@@ -3741,7 +3754,6 @@
null /* usesStaticLibraryVersions */,
null /* mimeGroups */, domainSetId);
packageSetting.setLastModifiedTime(timeStamp);
- packageSetting.setFirstInstallTime(firstInstallTime);
packageSetting.setLastUpdateTime(lastUpdateTime);
mPendingPackages.add(packageSetting);
if (PackageManagerService.DEBUG_SETTINGS)
@@ -3876,6 +3888,9 @@
XmlUtils.skipCurrentTag(parser);
}
}
+ if (firstInstallTime != 0) {
+ originalFirstInstallTimes.put(packageSetting.getPackageName(), firstInstallTime);
+ }
} else {
XmlUtils.skipCurrentTag(parser);
}
@@ -4471,8 +4486,6 @@
pw.print(",");
pw.print(ps.getVersionCode());
pw.print(",");
- pw.print(ps.getFirstInstallTime());
- pw.print(",");
pw.print(ps.getLastUpdateTime());
pw.print(",");
pw.print(ps.getInstallSource().installerPackageName != null
@@ -4516,6 +4529,8 @@
pw.print(",");
pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?");
pw.print(",");
+ pw.print(ps.readUserState(user.id).getFirstInstallTime());
+ pw.print(",");
pw.println();
}
return;
@@ -4746,9 +4761,6 @@
pw.print(prefix); pw.print(" timeStamp=");
date.setTime(ps.getLastModifiedTime());
pw.println(sdf.format(date));
- pw.print(prefix); pw.print(" firstInstallTime=");
- date.setTime(ps.getFirstInstallTime());
- pw.println(sdf.format(date));
pw.print(prefix); pw.print(" lastUpdateTime=");
date.setTime(ps.getLastUpdateTime());
pw.println(sdf.format(date));
@@ -4854,6 +4866,11 @@
pw.print(" installReason=");
pw.println(userState.getInstallReason());
+ final PackageUserStateInternal pus = ps.readUserState(user.id);
+ pw.print(" firstInstallTime=");
+ date.setTime(pus.getFirstInstallTime());
+ pw.println(sdf.format(date));
+
if (userState.isSuspended()) {
pw.print(prefix);
pw.println(" Suspend params:");
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index f38ae77..0055f4e 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -381,7 +381,7 @@
* @return The package setting that represents the latest version of shared library info.
*/
@Nullable
- PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
+ PackageSetting getStaticSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
PackageSetting sharedLibPackage = null;
synchronized (mPm.mLock) {
final SharedLibraryInfo latestSharedLibraVersionLPr =
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 3d10b6f..bf7ef1b 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -22,6 +22,7 @@
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSession;
+import android.app.appsearch.GetByDocumentIdRequest;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.PutDocumentsRequest;
import android.app.appsearch.RemoveByDocumentIdRequest;
@@ -820,42 +821,6 @@
getPinnedByAnyLauncher, si));
}
- /**
- * Find all shortcuts that has id matching {@code ids}.
- */
- public void findAllByIds(@NonNull final List<ShortcutInfo> result,
- @NonNull final Collection<String> ids, @Nullable final Predicate<ShortcutInfo> filter,
- final int cloneFlag) {
- findAllByIds(result, ids, filter, cloneFlag, null, 0, /*getPinnedByAnyLauncher=*/ false);
- }
-
- /**
- * Find all shortcuts that has id matching {@code ids}.
- *
- * This will also provide a "view" for each launcher -- a non-dynamic shortcut that's not pinned
- * by the calling launcher will not be included in the result, and also "isPinned" will be
- * adjusted for the caller too.
- */
- public void findAllByIds(@NonNull List<ShortcutInfo> result,
- @NonNull final Collection<String> ids, @Nullable final Predicate<ShortcutInfo> query,
- int cloneFlag, @Nullable String callingLauncher, int launcherUserId,
- boolean getPinnedByAnyLauncher) {
- if (getPackageInfo().isShadow()) {
- // Restored and the app not installed yet, so don't return any.
- return;
- }
- final ShortcutService s = mShortcutUser.mService;
-
- // Set of pinned shortcuts by the calling launcher.
- final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
- : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
- .getPinnedShortcutIds(getPackageName(), getPackageUserId());
- for (ShortcutInfo si : mShortcuts.values()) {
- filter(result, query, cloneFlag, callingLauncher, pinnedByCallerSet,
- getPinnedByAnyLauncher, si);
- }
- }
-
private void filter(@NonNull final List<ShortcutInfo> result,
@Nullable final Predicate<ShortcutInfo> query, final int cloneFlag,
@Nullable final String callingLauncher,
@@ -2411,6 +2376,25 @@
})));
}
+ void getShortcutByIdsAsync(@NonNull final Set<String> ids,
+ @NonNull final Consumer<List<ShortcutInfo>> cb) {
+ if (!isAppSearchEnabled()) {
+ cb.accept(Collections.emptyList());
+ return;
+ }
+ runAsSystem(() -> fromAppSearch().thenAccept(session -> {
+ session.getByDocumentId(new GetByDocumentIdRequest.Builder(getPackageName())
+ .addIds(ids).build(), mShortcutUser.mExecutor, result -> {
+ final List<ShortcutInfo> ret = result.getSuccesses().values()
+ .stream().map(doc ->
+ new AppSearchShortcutInfo(doc)
+ .toShortcutInfo(mShortcutUser.getUserId()))
+ .collect(Collectors.toList());
+ cb.accept(ret);
+ });
+ }));
+ }
+
private void removeShortcutAsync(@NonNull final String... id) {
Objects.requireNonNull(id);
removeShortcutAsync(Arrays.asList(id));
@@ -2444,9 +2428,8 @@
}
if (ShortcutService.DEBUG_REBOOT) {
Slog.d(TAG, "Saving shortcuts async for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName() + " ids=["
- + shortcuts.stream().map(ShortcutInfo::getId)
- .collect(Collectors.joining(",")) + "]");
+ + " pkg=" + getPackageName() + " ids=" + shortcuts.stream()
+ .map(ShortcutInfo::getId).collect(Collectors.joining(",", "[", "]")));
}
runAsSystem(() -> fromAppSearch().thenAccept(session -> {
if (shortcuts.isEmpty()) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a482f9a..0a2735cd 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -153,6 +153,7 @@
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* TODO:
@@ -2957,13 +2958,8 @@
final Predicate<ShortcutInfo> filter = getFilterFromQuery(ids, locusIds, changedSince,
componentName, queryFlags, getPinnedByAnyLauncher);
- if (ids != null && !ids.isEmpty()) {
- p.findAllByIds(ret, ids, filter, cloneFlag, callingPackage, launcherUserId,
+ p.findAll(ret, filter, cloneFlag, callingPackage, launcherUserId,
getPinnedByAnyLauncher);
- } else {
- p.findAll(ret, filter, cloneFlag, callingPackage, launcherUserId,
- getPinnedByAnyLauncher);
- }
}
private Predicate<ShortcutInfo> getFilterFromQuery(@Nullable ArraySet<String> ids,
@@ -3010,6 +3006,51 @@
}
@Override
+ public void getShortcutsAsync(int launcherUserId,
+ @NonNull String callingPackage, long changedSince,
+ @Nullable String packageName, @Nullable List<String> shortcutIds,
+ @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
+ int queryFlags, int userId, int callingPid, int callingUid,
+ @NonNull AndroidFuture<List<ShortcutInfo>> cb) {
+ final List<ShortcutInfo> ret = getShortcuts(launcherUserId, callingPackage,
+ changedSince, packageName, shortcutIds, locusIds, componentName, queryFlags,
+ userId, callingPid, callingUid);
+ if (shortcutIds == null || packageName == null || ret.size() >= shortcutIds.size()) {
+ // skip persistence layer if not querying by id in a specific package or all
+ // shortcuts have already been found.
+ cb.complete(ret);
+ return;
+ }
+ final ShortcutPackage p;
+ synchronized (mLock) {
+ p = getUserShortcutsLocked(userId).getPackageShortcutsIfExists(packageName);
+ }
+ if (p == null) {
+ cb.complete(ret);
+ return; // Bail-out directly if package doesn't exist.
+ }
+ // fetch remaining shortcuts from persistence layer
+ final ArraySet<String> ids = new ArraySet<>(shortcutIds);
+ // remove the ids that are already fetched
+ ret.stream().map(ShortcutInfo::getId).collect(Collectors.toList()).forEach(ids::remove);
+
+ int flags = ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
+ if ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0) {
+ flags = ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
+ } else if ((queryFlags & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
+ flags &= ~ShortcutInfo.CLONE_REMOVE_PERSON;
+ }
+ final int cloneFlag = flags;
+
+ p.getShortcutByIdsAsync(ids, shortcuts -> {
+ if (shortcuts != null) {
+ shortcuts.stream().map(si -> si.clone(cloneFlag)).forEach(ret::add);
+ }
+ cb.complete(ret);
+ });
+ }
+
+ @Override
public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -3047,12 +3088,32 @@
}
final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
- p.findAllByIds(list, Collections.singletonList(shortcutId),
- (ShortcutInfo si) -> shortcutId.equals(si.getId()),
+ p.findAll(list, (ShortcutInfo si) -> shortcutId.equals(si.getId()),
/* clone flags=*/ 0, callingPackage, launcherUserId, getPinnedByAnyLauncher);
return list.size() == 0 ? null : list.get(0);
}
+ private void getShortcutInfoAsync(
+ int launcherUserId, @NonNull String packageName, @NonNull String shortcutId,
+ int userId, @NonNull Consumer<ShortcutInfo> cb) {
+ Preconditions.checkStringNotEmpty(packageName, "packageName");
+ Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
+
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
+ final ShortcutPackage p;
+ synchronized (mLock) {
+ p = getUserShortcutsLocked(userId).getPackageShortcutsIfExists(packageName);
+ }
+ if (p == null) {
+ cb.accept(null);
+ return;
+ }
+ p.getShortcutByIdsAsync(Collections.singleton(shortcutId), shortcuts ->
+ cb.accept(shortcuts == null || shortcuts.isEmpty() ? null : shortcuts.get(0)));
+ }
+
@Override
public void pinShortcuts(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@@ -3237,6 +3298,48 @@
}
@Override
+ public void createShortcutIntentsAsync(int launcherUserId,
+ @NonNull String callingPackage, @NonNull String packageName,
+ @NonNull String shortcutId, int userId, int callingPid,
+ int callingUid, @NonNull AndroidFuture<Intent[]> cb) {
+ // Calling permission must be checked by LauncherAppsImpl.
+ Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
+ Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
+
+ // Check in memory shortcut first
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave();
+
+ final boolean getPinnedByAnyLauncher =
+ canSeeAnyPinnedShortcut(callingPackage, launcherUserId,
+ callingPid, callingUid);
+
+ // Make sure the shortcut is actually visible to the launcher.
+ final ShortcutInfo si = getShortcutInfoLocked(
+ launcherUserId, callingPackage, packageName, shortcutId, userId,
+ getPinnedByAnyLauncher);
+ if (si != null) {
+ if (!si.isEnabled() || !(si.isAlive() || getPinnedByAnyLauncher)) {
+ Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled");
+ cb.complete(null);
+ return;
+ }
+ cb.complete(si.getIntents());
+ return;
+ }
+ }
+
+ // Otherwise check persisted shortcuts
+ getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si -> {
+ cb.complete(si == null ? null : si.getIntents());
+ });
+ }
+
+ @Override
public void addListener(@NonNull ShortcutChangeListener listener) {
synchronized (mLock) {
mListeners.add(Objects.requireNonNull(listener));
@@ -3326,23 +3429,68 @@
}
final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
- if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
+ if (shortcutInfo == null) {
return null;
}
- final String path = mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcutInfo);
- if (path == null) {
- Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
- return null;
+ return getShortcutIconParcelFileDescriptor(shortcutInfo);
+ }
+ }
+
+ @Override
+ public void getShortcutIconFdAsync(int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId,
+ @NonNull AndroidFuture<ParcelFileDescriptor> cb) {
+ Objects.requireNonNull(callingPackage, "callingPackage");
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(shortcutId, "shortcutId");
+
+ // Checks shortcuts in memory first
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave();
+
+ final ShortcutPackage p = getUserShortcutsLocked(userId)
+ .getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ cb.complete(null);
+ return;
}
- try {
- return ParcelFileDescriptor.open(
- new File(path),
- ParcelFileDescriptor.MODE_READ_ONLY);
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "Icon file not found: " + path);
- return null;
+
+ final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
+ if (shortcutInfo != null) {
+ cb.complete(getShortcutIconParcelFileDescriptor(shortcutInfo));
+ return;
}
}
+
+ // Otherwise check persisted shortcuts
+ getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si -> {
+ cb.complete(getShortcutIconParcelFileDescriptor(si));
+ });
+ }
+
+ @Nullable
+ private ParcelFileDescriptor getShortcutIconParcelFileDescriptor(
+ @NonNull final ShortcutInfo shortcutInfo) {
+ if (!shortcutInfo.hasIconFile()) {
+ return null;
+ }
+ final String path = mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcutInfo);
+ if (path == null) {
+ Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
+ return null;
+ }
+ try {
+ return ParcelFileDescriptor.open(
+ new File(path),
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "Icon file not found: " + path);
+ return null;
+ }
}
@Override
@@ -3366,34 +3514,82 @@
}
final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
- if (shortcutInfo == null || !shortcutInfo.hasIconUri()) {
+ if (shortcutInfo == null) {
return null;
}
- String uri = shortcutInfo.getIconUri();
- if (uri == null) {
- Slog.w(TAG, "null uri detected in getShortcutIconUri()");
- return null;
+ return getShortcutIconUriInternal(launcherUserId, launcherPackage,
+ packageName, shortcutInfo, userId);
+ }
+ }
+
+ @Override
+ public void getShortcutIconUriAsync(int launcherUserId, @NonNull String launcherPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId,
+ @NonNull AndroidFuture<String> cb) {
+ Objects.requireNonNull(launcherPackage, "launcherPackage");
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(shortcutId, "shortcutId");
+
+ // Checks shortcuts in memory first
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
+ getLauncherShortcutsLocked(launcherPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave();
+
+ final ShortcutPackage p = getUserShortcutsLocked(userId)
+ .getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ cb.complete(null);
+ return;
}
- final long token = Binder.clearCallingIdentity();
- try {
- int packageUid = mPackageManagerInternal.getPackageUid(packageName,
- PackageManager.MATCH_DIRECT_BOOT_AUTO, userId);
- // Grant read uri permission to the caller on behalf of the shortcut owner. All
- // granted permissions are revoked when the default launcher changes, or when
- // device is rebooted.
- mUriGrantsManager.grantUriPermissionFromOwner(mUriPermissionOwner, packageUid,
- launcherPackage, Uri.parse(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION,
- userId, launcherUserId);
- } catch (Exception e) {
- Slog.e(TAG, "Failed to grant uri access to " + launcherPackage + " for " + uri,
- e);
- uri = null;
- } finally {
- Binder.restoreCallingIdentity(token);
+ final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
+ if (shortcutInfo != null) {
+ cb.complete(getShortcutIconUriInternal(launcherUserId, launcherPackage,
+ packageName, shortcutInfo, userId));
+ return;
}
- return uri;
}
+
+ // Otherwise check persisted shortcuts
+ getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si -> {
+ cb.complete(getShortcutIconUriInternal(launcherUserId, launcherPackage,
+ packageName, si, userId));
+ });
+ }
+
+ private String getShortcutIconUriInternal(int launcherUserId,
+ @NonNull String launcherPackage, @NonNull String packageName,
+ @NonNull ShortcutInfo shortcutInfo, int userId) {
+ if (!shortcutInfo.hasIconUri()) {
+ return null;
+ }
+ String uri = shortcutInfo.getIconUri();
+ if (uri == null) {
+ Slog.w(TAG, "null uri detected in getShortcutIconUri()");
+ return null;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int packageUid = mPackageManagerInternal.getPackageUid(packageName,
+ PackageManager.MATCH_DIRECT_BOOT_AUTO, userId);
+ // Grant read uri permission to the caller on behalf of the shortcut owner. All
+ // granted permissions are revoked when the default launcher changes, or when
+ // device is rebooted.
+ mUriGrantsManager.grantUriPermissionFromOwner(mUriPermissionOwner, packageUid,
+ launcherPackage, Uri.parse(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ userId, launcherUserId);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to grant uri access to " + launcherPackage + " for " + uri,
+ e);
+ uri = null;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return uri;
}
@Override
@@ -5154,7 +5350,7 @@
}
List<ShortcutInfo> result = new ArrayList<>();
- ps.findAllByIds(result, resultIds, (ShortcutInfo si) -> resultIds.contains(si.getId()),
+ ps.findAll(result, (ShortcutInfo si) -> resultIds.contains(si.getId()),
ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
return result;
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 9cb8863..8a6ef6b 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -32,7 +32,7 @@
import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.StagedApexInfo;
@@ -129,7 +129,7 @@
boolean containsApkSession();
boolean containsApexSession();
void setSessionReady();
- void setSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage);
+ void setSessionFailed(@SessionErrorCode int errorCode, String errorMessage);
void setSessionApplied();
void installSession(IntentSender statusReceiver);
boolean hasParentSessionId();
@@ -932,9 +932,7 @@
info.diskImagePath = ai.modulePath;
info.versionCode = ai.versionCode;
info.versionName = ai.versionName;
- info.hasBootClassPathJars = ai.hasBootClassPathJars;
- info.hasDex2OatBootClassPathJars = ai.hasDex2OatBootClassPathJars;
- info.hasSystemServerClassPathJars = ai.hasSystemServerClassPathJars;
+ info.hasClassPathJars = ai.hasClassPathJars;
return info;
}
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index a4f8087..b15e495 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -810,7 +810,7 @@
} else {
grantPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackage(pm, ACTION_TRACK, userId), userId,
- SENSORS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
+ SENSORS_PERMISSIONS);
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 34575e0..f5ee8d9 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -100,12 +100,6 @@
String getCpuAbiOverride();
/**
- * In epoch milliseconds. The timestamp of the first install of the particular app on the
- * device, surviving past app updates. This does not survive full uninstalls + reinstalls.
- */
- long getFirstInstallTime();
-
- /**
* In epoch milliseconds. The last modified time of the file directory which houses the app
* APKs. Only updated on package update; does not track realtime modifications.
*/
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index f5e498d..a5d399e 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -115,7 +115,6 @@
private final int mCategoryOverride;
@Nullable
private final String mCpuAbiOverride;
- private final long mFirstInstallTime;
private final long mLastModifiedTime;
private final long mLastUpdateTime;
private final long mLongVersionCode;
@@ -166,7 +165,6 @@
mAppId = pkgState.getAppId();
mCategoryOverride = pkgState.getCategoryOverride();
mCpuAbiOverride = pkgState.getCpuAbiOverride();
- mFirstInstallTime = pkgState.getFirstInstallTime();
mLastModifiedTime = pkgState.getLastModifiedTime();
mLastUpdateTime = pkgState.getLastUpdateTime();
mLongVersionCode = pkgState.getVersionCode();
@@ -341,6 +339,7 @@
private final int mUninstallReason;
@Nullable
private final String mSplashScreenTheme;
+ private final long mFirstInstallTime;
private UserStateImpl(@NonNull PackageUserState userState) {
mCeDataInode = userState.getCeDataInode();
@@ -362,6 +361,7 @@
setBoolean(Booleans.STOPPED, userState.isStopped());
setBoolean(Booleans.SUSPENDED, userState.isSuspended());
setBoolean(Booleans.VIRTUAL_PRELOAD, userState.isVirtualPreload());
+ mFirstInstallTime = userState.getFirstInstallTime();
}
@Override
@@ -505,16 +505,21 @@
}
@DataClass.Generated.Member
+ public long getFirstInstallTime() {
+ return mFirstInstallTime;
+ }
+
+ @DataClass.Generated.Member
public @NonNull UserStateImpl setBooleans( int value) {
mBooleans = value;
return this;
}
@DataClass.Generated(
- time = 1637977288540L,
+ time = 1640209608883L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final long mFirstInstallTime\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
@@ -575,11 +580,6 @@
}
@DataClass.Generated.Member
- public long getFirstInstallTime() {
- return mFirstInstallTime;
- }
-
- @DataClass.Generated.Member
public long getLastModifiedTime() {
return mLastModifiedTime;
}
@@ -671,10 +671,10 @@
}
@DataClass.Generated(
- time = 1637977288579L,
+ time = 1640209608912L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mFirstInstallTime\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
index 09b9d31..6c8e0b7 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.pkg.PackageUserStateUtils;
+import android.util.SparseArray;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -75,4 +76,23 @@
return PackageUserStateUtils.isMatch(userState, packageState.isSystem(),
pkg.isEnabled(), component, flags);
}
+
+ /**
+ * Return the earliest non-zero first-install timestamp of an installed app among all the users,
+ * unless none of the users have a non-zero first-install timestamp. In that case, return 0.
+ */
+ public static long getEarliestFirstInstallTime(
+ @Nullable SparseArray<? extends PackageUserStateInternal> userStatesInternal) {
+ if (userStatesInternal == null || userStatesInternal.size() == 0) {
+ return 0;
+ }
+ long earliestFirstInstallTime = Long.MAX_VALUE;
+ for (int i = 0; i < userStatesInternal.size(); i++) {
+ final long firstInstallTime = userStatesInternal.valueAt(i).getFirstInstallTime();
+ if (firstInstallTime != 0 && firstInstallTime < earliestFirstInstallTime) {
+ earliestFirstInstallTime = firstInstallTime;
+ }
+ }
+ return earliestFirstInstallTime == Long.MAX_VALUE ? 0 : earliestFirstInstallTime;
+ }
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index 147edf7..03b1692 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -150,4 +150,13 @@
*/
@Nullable
String getSplashScreenTheme();
+
+ /**
+ * In epoch milliseconds. The timestamp of the first install of the app of the particular user
+ * on the device, surviving past app updates. Different users might have a different first
+ * install time.
+ *
+ * This does not survive full removal of the app (i.e., uninstalls for all users).
+ */
+ long getFirstInstallTime();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index 481c3e0..73c86c7 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -134,6 +134,11 @@
}
@Override
+ public long getFirstInstallTime() {
+ return 0;
+ }
+
+ @Override
public boolean isComponentEnabled(String componentName) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index 6fafb24..25abcb3 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -82,6 +82,8 @@
@Nullable
private ArrayMap<ComponentName, Pair<String, Integer>> mComponentLabelIconOverrideMap;
+ private long mFirstInstallTime;
+
public PackageUserStateImpl() {
super();
}
@@ -401,6 +403,11 @@
}
@DataClass.Generated.Member
+ public long getFirstInstallTime() {
+ return mFirstInstallTime;
+ }
+
+ @DataClass.Generated.Member
public @NonNull PackageUserStateImpl setDisabledComponents(@NonNull ArraySet<String> value) {
mDisabledComponents = value;
return this;
@@ -521,6 +528,12 @@
return this;
}
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setFirstInstallTime( long value) {
+ mFirstInstallTime = value;
+ return this;
+ }
+
@Override
@DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
@@ -554,7 +567,8 @@
&& Objects.equals(mSplashScreenTheme, that.mSplashScreenTheme)
&& Objects.equals(mSuspendParams, that.mSuspendParams)
&& Objects.equals(mCachedOverlayPaths, that.mCachedOverlayPaths)
- && Objects.equals(mComponentLabelIconOverrideMap, that.mComponentLabelIconOverrideMap);
+ && Objects.equals(mComponentLabelIconOverrideMap, that.mComponentLabelIconOverrideMap)
+ && mFirstInstallTime == that.mFirstInstallTime;
}
@Override
@@ -585,14 +599,15 @@
_hash = 31 * _hash + Objects.hashCode(mSuspendParams);
_hash = 31 * _hash + Objects.hashCode(mCachedOverlayPaths);
_hash = 31 * _hash + Objects.hashCode(mComponentLabelIconOverrideMap);
+ _hash = 31 * _hash + Long.hashCode(mFirstInstallTime);
return _hash;
}
@DataClass.Generated(
- time = 1636145886996L,
+ time = 1640923839971L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
- inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+ inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate long mFirstInstallTime\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index a3b0e3e..d1603f5 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -61,6 +61,7 @@
import com.android.server.pm.Settings;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
@@ -820,10 +821,11 @@
PackageStateInternal firstPkgSetting = pkgSettingFunction.apply(first);
PackageStateInternal secondPkgSetting = pkgSettingFunction.apply(second);
- long firstInstallTime =
- firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime();
- long secondInstallTime =
- secondPkgSetting == null ? -1L : secondPkgSetting.getFirstInstallTime();
+ long firstInstallTime = firstPkgSetting == null
+ ? -1L : firstPkgSetting.getUserStateOrDefault(userId).getFirstInstallTime();
+ long secondInstallTime = secondPkgSetting == null
+ ? -1L
+ : secondPkgSetting.getUserStateOrDefault(userId).getFirstInstallTime();
if (firstInstallTime != secondInstallTime) {
return (int) (firstInstallTime - secondInstallTime);
@@ -1650,7 +1652,8 @@
continue;
}
- long installTime = pkgSetting.getFirstInstallTime();
+ long installTime = PackageStateUtils.getEarliestFirstInstallTime(
+ pkgSetting.getUserStates());
if (installTime > latestInstall) {
latestInstall = installTime;
targetPackageName = packageName;
@@ -1977,7 +1980,7 @@
if (pkgSetting == null) {
continue;
}
- long installTime = pkgSetting.getFirstInstallTime();
+ long installTime = pkgSetting.getUserStateOrDefault(userId).getFirstInstallTime();
if (installTime > latestInstall) {
latestInstall = installTime;
filteredPackages.clear();
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 268de3e..68e078c 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -17,11 +17,13 @@
import static android.view.KeyEvent.KEYCODE_POWER;
+import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseLongArray;
import android.view.KeyEvent;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ToBooleanFunction;
import java.io.PrintWriter;
@@ -35,13 +37,18 @@
private static final String TAG = "KeyCombinationManager";
// Store the received down time of keycode.
+ @GuardedBy("mLock")
private final SparseLongArray mDownTimes = new SparseLongArray(2);
private final ArrayList<TwoKeysCombinationRule> mRules = new ArrayList();
// Selected rules according to current key down.
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
private final ArrayList<TwoKeysCombinationRule> mActiveRules = new ArrayList();
// The rule has been triggered by current keys.
+ @GuardedBy("mLock")
private TwoKeysCombinationRule mTriggeredRule;
+ private final Handler mHandler = new Handler();
// Keys in a key combination must be pressed within this interval of each other.
private static final long COMBINE_KEY_DELAY_MILLIS = 150;
@@ -109,6 +116,12 @@
* Return true if any active rule could be triggered by the key event, otherwise false.
*/
boolean interceptKey(KeyEvent event, boolean interactive) {
+ synchronized (mLock) {
+ return interceptKeyLocked(event, interactive);
+ }
+ }
+
+ private boolean interceptKeyLocked(KeyEvent event, boolean interactive) {
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final int keyCode = event.getKeyCode();
final int count = mActiveRules.size();
@@ -154,7 +167,7 @@
return false;
}
Log.v(TAG, "Performing combination rule : " + rule);
- rule.execute();
+ mHandler.post(rule::execute);
mTriggeredRule = rule;
return true;
});
@@ -169,7 +182,7 @@
for (int index = count - 1; index >= 0; index--) {
final TwoKeysCombinationRule rule = mActiveRules.get(index);
if (rule.shouldInterceptKey(keyCode)) {
- rule.cancel();
+ mHandler.post(rule::cancel);
mActiveRules.remove(index);
}
}
@@ -181,31 +194,37 @@
* Return the interceptTimeout to tell InputDispatcher when is ready to deliver to window.
*/
long getKeyInterceptTimeout(int keyCode) {
- if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) {
- return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
+ synchronized (mLock) {
+ if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) {
+ return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
+ }
+ return 0;
}
- return 0;
}
/**
* True if the key event had been handled.
*/
boolean isKeyConsumed(KeyEvent event) {
- if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
- return false;
+ synchronized (mLock) {
+ if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
+ return false;
+ }
+ return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode());
}
- return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode());
}
/**
* True if power key is the candidate.
*/
boolean isPowerKeyIntercepted() {
- if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) {
- // return false if only if power key pressed.
- return mDownTimes.size() > 1 || mDownTimes.get(KEYCODE_POWER) == 0;
+ synchronized (mLock) {
+ if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) {
+ // return false if only if power key pressed.
+ return mDownTimes.size() > 1 || mDownTimes.get(KEYCODE_POWER) == 0;
+ }
+ return false;
}
- return false;
}
/**
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index a5969a8..54ece73 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -138,7 +138,8 @@
// By default CLOSE_SYSTEM_DIALOGS broadcast is sent only for current user, which is user
// 10 on devices with headless system user enabled.
// In order to receive the broadcast, register the broadcast receiver with UserHandle.ALL.
- context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null,
+ Context.RECEIVER_EXPORTED);
mHasTelephony =
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index e857d32..784e177 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -112,10 +112,15 @@
* @return The intent that matches the shortcut, or null if not found.
*/
private Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
+ // If a modifier key other than shift is also pressed, skip it.
+ final boolean isShiftOn = KeyEvent.metaStateHasModifiers(metaState, KeyEvent.META_SHIFT_ON);
+ if (!isShiftOn && !KeyEvent.metaStateHasNoModifiers(metaState)) {
+ return null;
+ }
+
ShortcutInfo shortcut = null;
// If the Shift key is pressed, then search for the shift shortcuts.
- boolean isShiftOn = (metaState & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON;
SparseArray<ShortcutInfo> shortcutMap = isShiftOn ? mShiftShortcuts : mIntentShortcuts;
// First try the exact keycode (with modifiers).
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4cced17..28f65cf 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2696,6 +2696,7 @@
final boolean canceled = event.isCanceled();
final int displayId = event.getDisplayId();
final long key_consumed = -1;
+ final long key_not_consumed = 0;
if (DEBUG_INPUT) {
Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
@@ -2885,7 +2886,7 @@
case KeyEvent.KEYCODE_TAB:
if (event.isMetaPressed()) {
// Pass through keyboard navigation keys.
- return 0;
+ return key_not_consumed;
}
// Display task switcher for ALT-TAB.
if (down && repeatCount == 0) {
@@ -2916,9 +2917,9 @@
return key_consumed;
case KeyEvent.KEYCODE_SPACE:
- // Handle keyboard layout switching.
- if ((metaState & (KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK)) == 0) {
- return 0;
+ // Handle keyboard layout switching. (META + SPACE)
+ if ((metaState & KeyEvent.META_META_MASK) == 0) {
+ return key_not_consumed;
}
// Share the same behavior with KEYCODE_LANGUAGE_SWITCH.
case KeyEvent.KEYCODE_LANGUAGE_SWITCH:
@@ -2992,7 +2993,7 @@
}
// Let the application handle the key.
- return 0;
+ return key_not_consumed;
}
/**
@@ -3058,6 +3059,10 @@
+ ", policyFlags=" + policyFlags);
}
+ if (interceptUnhandledKey(event)) {
+ return null;
+ }
+
KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
@@ -3112,13 +3117,46 @@
return fallbackEvent;
}
+ private boolean interceptUnhandledKey(KeyEvent event) {
+ final int keyCode = event.getKeyCode();
+ final int repeatCount = event.getRepeatCount();
+ final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ final int metaState = event.getModifiers();
+
+ switch(keyCode) {
+ case KeyEvent.KEYCODE_SPACE:
+ if (down && repeatCount == 0) {
+ // Handle keyboard layout switching. (CTRL + SPACE)
+ if (KeyEvent.metaStateHasModifiers(metaState, KeyEvent.META_CTRL_ON)) {
+ int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
+ mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
+ return true;
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_Z:
+ if (down && KeyEvent.metaStateHasModifiers(metaState,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)) {
+ // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
+ if (mAccessibilityShortcutController
+ .isAccessibilityShortcutAvailable(isKeyguardLocked())) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
+ return true;
+ }
+ }
+ break;
+ }
+
+ return false;
+ }
+
private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
focusedToken, fallbackEvent, policyFlags);
- if (delayMillis == 0) {
+ if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent)) {
return true;
}
}
@@ -3989,19 +4027,6 @@
}
}
- // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
- if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(isKeyguardLocked())) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_Z: {
- if (down && event.isCtrlPressed() && event.isAltPressed()) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
- result &= ~ACTION_PASS_TO_USER;
- }
- break;
- }
- }
- }
-
if (useHapticFeedback) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
"Virtual Key - Press");
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index e94575c..b03db66 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -213,7 +213,7 @@
CloseDialogReceiver(Context context) {
mContext = context;
IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- context.registerReceiver(this, filter);
+ context.registerReceiver(this, filter, Context.RECEIVER_EXPORTED);
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java b/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
index 7f047f8..fba0fb4 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
@@ -20,8 +20,6 @@
import android.annotation.Nullable;
import java.lang.reflect.Array;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Map;
@@ -33,67 +31,28 @@
static public final int kDefaultMaxCollectionLength = 16;
/**
- * Simple version of {@link #print(Object, boolean, int)} that prints an object, without
- * recursing into sub-objects.
- *
- * @param obj The object to print.
- * @return A string representing the object.
- */
- static String print(@Nullable Object obj) {
- return print(obj, false, kDefaultMaxCollectionLength);
- }
-
- /**
* Pretty-prints an object.
*
* @param obj The object to print.
- * @param deep Whether to pretty-print sub-objects (if false, just prints them
- * with {@link Object#toString()}).
* @param maxCollectionLength Whenever encountering collections, maximum number of elements to
* print.
* @return A string representing the object.
*/
- static String print(@Nullable Object obj, boolean deep, int maxCollectionLength) {
+ static String print(@Nullable Object obj, int maxCollectionLength) {
StringBuilder builder = new StringBuilder();
- print(builder, obj, deep, maxCollectionLength);
+ print(builder, obj, maxCollectionLength);
return builder.toString();
}
/**
- * This version is suitable for use inside a toString() override of an object, e.g.:
- * <pre><code>
- * class MyObject {
- * ...
- * @Override
- * String toString() {
- * return ObjectPrinter.printPublicFields(this, ...);
- * }
- * }
- * </code></pre>
- *
- * @param obj The object to print.
- * @param deep Whether to pretty-print sub-objects (if false, just prints them
- * with {@link Object#toString()}).
- * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
- * print.
- */
- static String printPublicFields(@Nullable Object obj, boolean deep, int maxCollectionLength) {
- StringBuilder builder = new StringBuilder();
- printPublicFields(builder, obj, deep, maxCollectionLength);
- return builder.toString();
- }
-
- /**
- * A version of {@link #print(Object, boolean, int)} that uses a {@link StringBuilder}.
+ * A version of {@link #print(Object, int)} that uses a {@link StringBuilder}.
*
* @param builder StringBuilder to print into.
* @param obj The object to print.
- * @param deep Whether to pretty-print sub-objects (if false, just prints them
- * with {@link Object#toString()}).
* @param maxCollectionLength Whenever encountering collections, maximum number of elements to
* print.
*/
- static void print(@NonNull StringBuilder builder, @Nullable Object obj, boolean deep,
+ static void print(@NonNull StringBuilder builder, @Nullable Object obj,
int maxCollectionLength) {
try {
if (obj == null) {
@@ -101,16 +60,16 @@
return;
}
if (obj instanceof Boolean) {
- builder.append(obj.toString());
+ builder.append(obj);
return;
}
if (obj instanceof Number) {
- builder.append(obj.toString());
+ builder.append(obj);
return;
}
if (obj instanceof Character) {
builder.append('\'');
- builder.append(obj.toString());
+ builder.append(obj);
builder.append('\'');
return;
}
@@ -137,7 +96,7 @@
isLong = true;
break;
}
- print(builder, child, deep, maxCollectionLength);
+ print(builder, child, maxCollectionLength);
++i;
}
if (isLong) {
@@ -163,9 +122,9 @@
isLong = true;
break;
}
- print(builder, child.getKey(), deep, maxCollectionLength);
+ print(builder, child.getKey(), maxCollectionLength);
builder.append(": ");
- print(builder, child.getValue(), deep, maxCollectionLength);
+ print(builder, child.getValue(), maxCollectionLength);
++i;
}
if (isLong) {
@@ -189,7 +148,7 @@
isLong = true;
break;
}
- print(builder, Array.get(obj, i), deep, maxCollectionLength);
+ print(builder, Array.get(obj, i), maxCollectionLength);
}
if (isLong) {
builder.append("... (+");
@@ -200,48 +159,7 @@
return;
}
- if (!deep) {
- builder.append(obj.toString());
- return;
- }
- printPublicFields(builder, obj, deep, maxCollectionLength);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * A version of {@link #printPublicFields(Object, boolean, int)} that uses a {@link
- * StringBuilder}.
- *
- * @param obj The object to print.
- * @param deep Whether to pretty-print sub-objects (if false, just prints them
- * with {@link Object#toString()}).
- * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
- * print.
- */
- static void printPublicFields(@NonNull StringBuilder builder, @Nullable Object obj,
- boolean deep,
- int maxCollectionLength) {
- try {
- Class cls = obj.getClass();
- builder.append("{ ");
-
- boolean first = true;
- for (Field fld : cls.getDeclaredFields()) {
- int mod = fld.getModifiers();
- if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.STATIC) == 0) {
- if (first) {
- first = false;
- } else {
- builder.append(", ");
- }
- builder.append(fld.getName());
- builder.append(": ");
- print(builder, fld.get(obj), deep, maxCollectionLength);
- }
- }
- builder.append(" }");
+ builder.append(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 559e777..dc4bdaa 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -18,14 +18,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
import android.media.soundtrigger.ModelParameterRange;
import android.media.soundtrigger.PhraseRecognitionEvent;
import android.media.soundtrigger.PhraseSoundModel;
import android.media.soundtrigger.RecognitionConfig;
import android.media.soundtrigger.RecognitionEvent;
import android.media.soundtrigger.SoundModel;
-import android.media.permission.Identity;
-import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
@@ -387,7 +387,7 @@
}
private static void printObject(@NonNull StringBuilder builder, @Nullable Object obj) {
- ObjectPrinter.print(builder, obj, true, 16);
+ ObjectPrinter.print(builder, obj, 16);
}
private static String printObject(@Nullable Object obj) {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index 76927e1..f3d151f 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -21,9 +21,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.content.Context;
import android.content.PermissionChecker;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
+import android.media.permission.PermissionUtil;
import android.media.soundtrigger.ModelParameterRange;
import android.media.soundtrigger.PhraseRecognitionEvent;
import android.media.soundtrigger.PhraseSoundModel;
@@ -31,9 +33,6 @@
import android.media.soundtrigger.RecognitionEvent;
import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger.Status;
-import android.media.permission.Identity;
-import android.media.permission.IdentityContext;
-import android.media.permission.PermissionUtil;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -144,7 +143,7 @@
if (status != PermissionChecker.PERMISSION_GRANTED) {
throw new SecurityException(
String.format("Failed to obtain permission %s for identity %s", permission,
- ObjectPrinter.print(identity, true, 16)));
+ ObjectPrinter.print(identity, 16)));
}
}
@@ -168,7 +167,7 @@
case PermissionChecker.PERMISSION_HARD_DENIED:
throw new SecurityException(
String.format("Failed to obtain permission %s for identity %s", permission,
- ObjectPrinter.print(identity, true, 16)));
+ ObjectPrinter.print(identity, 16)));
default:
throw new RuntimeException("Unexpected perimission check result.");
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 4243fc7..09035cd 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
import android.media.soundtrigger.ModelParameterRange;
import android.media.soundtrigger.PhraseRecognitionEvent;
import android.media.soundtrigger.PhraseSoundModel;
@@ -27,8 +29,6 @@
import android.media.soundtrigger.RecognitionStatus;
import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger.Status;
-import android.media.permission.Identity;
-import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -230,7 +230,7 @@
final ModuleState module = mModules.get(handle);
pw.println("=========================================");
pw.printf("Module %d\n%s\n", handle,
- ObjectPrinter.print(module.properties, true, 16));
+ ObjectPrinter.print(module.properties, 16));
pw.println("=========================================");
for (Session session : module.sessions) {
session.dump(pw);
@@ -250,11 +250,11 @@
/** State of a sound model. */
static class ModelState {
ModelState(SoundModel model) {
- this.description = ObjectPrinter.print(model, true, 16);
+ this.description = ObjectPrinter.print(model, 16);
}
ModelState(PhraseSoundModel model) {
- this.description = ObjectPrinter.print(model, true, 16);
+ this.description = ObjectPrinter.print(model, 16);
}
/** Activity state of a sound model. */
@@ -690,8 +690,8 @@
if (mState == ModuleStatus.ALIVE) {
pw.println("-------------------------------");
pw.printf("Session %s, client: %s\n", toString(),
- ObjectPrinter.print(mOriginatorIdentity, true, 16));
- pw.printf("Loaded models (handle, active, description):", toString());
+ ObjectPrinter.print(mOriginatorIdentity, 16));
+ pw.println("Loaded models (handle, active, description):");
pw.println();
pw.println("-------------------------------");
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 4eae939..54ec884 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -18,6 +18,9 @@
import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
+import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE;
+import static android.app.StatusBarManager.NavBarModeOverride;
import static android.view.Display.DEFAULT_DISPLAY;
import android.Manifest;
@@ -59,6 +62,7 @@
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.notification.NotificationStats;
import android.service.quicksettings.TileService;
import android.text.TextUtils;
@@ -1846,6 +1850,51 @@
return mContext.getResources().getStringArray(R.array.config_statusBarIcons);
}
+ /**
+ * Sets or removes the navigation bar mode override.
+ *
+ * @param navBarModeOverride the mode of the navigation bar override to be set.
+ */
+ public void setNavBarModeOverride(@NavBarModeOverride int navBarModeOverride) {
+ enforceStatusBar();
+ if (navBarModeOverride != NAV_BAR_MODE_OVERRIDE_NONE
+ && navBarModeOverride != NAV_BAR_MODE_OVERRIDE_KIDS) {
+ throw new UnsupportedOperationException(
+ "Supplied navBarModeOverride not supported: " + navBarModeOverride);
+ }
+
+ final int userId = mCurrentUserId;
+ final long userIdentity = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.NAV_BAR_KIDS_MODE, navBarModeOverride, userId);
+ } finally {
+ Binder.restoreCallingIdentity(userIdentity);
+ }
+ }
+
+ /**
+ * Gets the navigation bar mode override. Returns default value if no override is set.
+ *
+ * @hide
+ */
+ public @NavBarModeOverride int getNavBarModeOverride() {
+ enforceStatusBar();
+
+ int navBarKidsMode = NAV_BAR_MODE_OVERRIDE_NONE;
+ final int userId = mCurrentUserId;
+ final long userIdentity = Binder.clearCallingIdentity();
+ try {
+ navBarKidsMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.NAV_BAR_KIDS_MODE, userId);
+ } catch (Settings.SettingNotFoundException ex) {
+ return navBarKidsMode;
+ } finally {
+ Binder.restoreCallingIdentity(userIdentity);
+ }
+ return navBarKidsMode;
+ }
+
/** @hide */
public void passThroughShellCommand(String[] args, FileDescriptor fd) {
enforceStatusBarOrShell();
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 39d7a15..7697490 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -177,7 +177,7 @@
}
}
}
- }, intentFilter);
+ }, intentFilter, Context.RECEIVER_NOT_EXPORTED);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 30465af..0b0b704 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6905,8 +6905,7 @@
getSyncTransaction().hide(mSurfaceControl);
}
if (show) {
- mActivityRecordInputSink.applyChangesToSurfaceIfChanged(
- getSyncTransaction(), mSurfaceControl);
+ mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getSyncTransaction());
}
}
if (mThumbnail != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index bce2883..9353f6d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -60,7 +60,7 @@
// Hold on to InputEventReceiver to prevent it from getting GCd.
private InputEventReceiver mInputEventReceiver;
private InputWindowHandleWrapper mInputWindowHandleWrapper;
-
+ private SurfaceControl mSurfaceControl;
private int mRapidTouchCount = 0;
private IBinder mToken;
private boolean mDisabled = false;
@@ -73,12 +73,25 @@
+ mActivityRecord.mActivityComponent.getShortClassName();
}
- public void applyChangesToSurfaceIfChanged(
- SurfaceControl.Transaction transaction, SurfaceControl surfaceControl) {
+ public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) {
InputWindowHandleWrapper inputWindowHandleWrapper = getInputWindowHandleWrapper();
- if (inputWindowHandleWrapper.isChanged()) {
- inputWindowHandleWrapper.applyChangesToSurface(transaction, surfaceControl);
+ if (mSurfaceControl == null) {
+ mSurfaceControl = createSurface(transaction);
}
+ if (inputWindowHandleWrapper.isChanged()) {
+ inputWindowHandleWrapper.applyChangesToSurface(transaction, mSurfaceControl);
+ }
+ }
+
+ private SurfaceControl createSurface(SurfaceControl.Transaction t) {
+ SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null)
+ .setName(mName)
+ .setHidden(false)
+ .setCallsite("ActivityRecordInputSink.createSurface")
+ .build();
+ // Put layer below all siblings (and the parent surface too)
+ t.setLayer(surfaceControl, Integer.MIN_VALUE);
+ return surfaceControl;
}
private InputWindowHandleWrapper getInputWindowHandleWrapper() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 51c8daf..3cecce2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -641,22 +641,35 @@
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
- // Don't debug things in the system process
- if (!aInfo.processName.equals("system")) {
- if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING
- | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) {
-
+ final boolean requestDebug = (startFlags & (START_FLAG_DEBUG
+ | START_FLAG_NATIVE_DEBUGGING | START_FLAG_TRACK_ALLOCATION)) != 0;
+ final boolean requestProfile = profilerInfo != null;
+ if (requestDebug || requestProfile) {
+ final boolean debuggable = (Build.IS_DEBUGGABLE
+ || (aInfo.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0)
+ && !aInfo.processName.equals("system");
+ if ((requestDebug && !debuggable) || (requestProfile
+ && (!debuggable && !aInfo.applicationInfo.isProfileableByShell()))) {
+ Slog.w(TAG, "Ignore debugging for non-debuggable app: " + aInfo.packageName);
+ } else {
// Mimic an AMS synchronous call by passing a message to AMS and wait for AMS
// to notify us that the task has completed.
// TODO(b/80414790) look into further untangling for the situation where the
// caller is on the same thread as the handler we are posting to.
synchronized (mService.mGlobalLock) {
// Post message to AMS.
- final Message msg = PooledLambda.obtainMessage(
- ActivityManagerInternal::setDebugFlagsForStartingActivity,
- mService.mAmInternal, aInfo, startFlags, profilerInfo,
- mService.mGlobalLock);
- mService.mH.sendMessage(msg);
+ mService.mH.post(() -> {
+ try {
+ mService.mAmInternal.setDebugFlagsForStartingActivity(aInfo,
+ startFlags, profilerInfo, mService.mGlobalLock);
+ } catch (Throwable e) {
+ // Simply ignore it because the debugging doesn't take effect.
+ Slog.w(TAG, e);
+ synchronized (mService.mGlobalLockWithoutBoost) {
+ mService.mGlobalLockWithoutBoost.notifyAll();
+ }
+ }
+ });
try {
mService.mGlobalLock.wait();
} catch (InterruptedException ignore) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 721907c..3d37278 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -799,6 +799,22 @@
}
/**
+ * Returns {@code true} if a given {@link WindowContainer} is an embedded Task.
+ *
+ * Note that this is a short term workaround to support Android Auto until it migrate to
+ * ShellTransition. This should only be used by {@link #getAnimationTargets}.
+ *
+ * TODO(b/213312721): Remove this predicate and its callers once ShellTransition is enabled.
+ */
+ private static boolean isEmbeddedTask(WindowContainer wc) {
+ // We use Task#mRemoveWithTaskOrganizer to identify an embedded Task, but this is a hack and
+ // it is not guaranteed to work this logic in the future version.
+ return !WindowManagerService.sEnableShellTransitions
+ && wc instanceof Task
+ && ((Task) wc).mRemoveWithTaskOrganizer;
+ }
+
+ /**
* Find WindowContainers to be animated from a set of opening and closing apps. We will promote
* animation targets to higher level in the window hierarchy if possible.
*
@@ -844,7 +860,13 @@
siblings.add(current);
boolean canPromote = true;
- if (parent == null || !parent.canCreateRemoteAnimationTarget()
+ if (isEmbeddedTask(current)) {
+ // Don't animate an embedded Task in app transition. This is a short term workaround
+ // to prevent conflict of surface hierarchy changes between legacy app transition
+ // and TaskView (b/205189147).
+ // TODO(b/213312721): Remove this once ShellTransition is enabled.
+ continue;
+ } else if (parent == null || !parent.canCreateRemoteAnimationTarget()
|| !parent.canBeAnimationTarget()
// We cannot promote the animation on Task's parent when the task is in
// clearing task in case the animating get stuck when performing the opening
@@ -887,7 +909,13 @@
for (int j = 0; j < parent.getChildCount(); ++j) {
final WindowContainer sibling = parent.getChildAt(j);
if (candidates.remove(sibling)) {
- siblings.add(sibling);
+ if (!isEmbeddedTask(sibling)) {
+ // Don't animate an embedded Task in app transition. This is a short
+ // term workaround to prevent conflict of surface hierarchy changes
+ // between legacy app transition and TaskView (b/205189147).
+ // TODO(b/213312721): Remove this once ShellTransition is enabled.
+ siblings.add(sibling);
+ }
} else if (sibling != current && sibling.isVisible()) {
canPromote = false;
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index e33c440..1a1101e 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -187,7 +187,6 @@
// Navigation bar doesn't get influenced by anything else
if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
- state.removeSource(ITYPE_IME);
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_CLIMATE_BAR);
state.removeSource(ITYPE_CAPTION_BAR);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fad87e8..659c771 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4994,8 +4994,7 @@
if (topFragment == f) {
return;
}
- if (!f.isFocusableAndVisible()) {
- // No need to resume activity in TaskFragment that is not visible.
+ if (!f.canBeResumed(null /* starting */)) {
return;
}
resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 796a90a..dfb559f 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -38,7 +38,6 @@
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK;
import static com.android.server.wm.DisplayContent.alwaysCreateRootTask;
-import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -1407,9 +1406,7 @@
leafTask.forAllLeafTaskFragments((taskFrag) -> {
final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
- if (resumedActivity != null
- && (taskFrag.getVisibility(resuming) != TASK_FRAGMENT_VISIBILITY_VISIBLE
- || !taskFrag.isTopActivityFocusable())) {
+ if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
someActivityPaused[0]++;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 97cb512..8299cea 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1355,6 +1355,17 @@
return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE;
}
+ /**
+ * Returns {@code true} is the activity in this TaskFragment can be resumed.
+ *
+ * @param starting The currently starting activity or {@code null} if there is none.
+ */
+ boolean canBeResumed(@Nullable ActivityRecord starting) {
+ // No need to resume activity in TaskFragment that is not visible.
+ return isTopActivityFocusable()
+ && getVisibility(starting) == TASK_FRAGMENT_VISIBILITY_VISIBLE;
+ }
+
boolean isFocusableAndVisible() {
return isTopActivityFocusable() && shouldBeVisible(null /* starting */);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 863e3ca..573ff2f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2547,7 +2547,8 @@
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG_WM, "isVisibleRequestedOrAdding " + this + ": "
- + isVisibleRequestedOrAdding());
+ + isVisibleRequestedOrAdding() + " isVisible: " + (isVisible()
+ && mActivityRecord != null && mActivityRecord.isVisible()));
if (!isVisibleRequestedOrAdding()) {
Slog.i(TAG_WM, " mSurfaceController=" + mWinAnimator.mSurfaceController
+ " relayoutCalled=" + mRelayoutCalled
@@ -2562,7 +2563,8 @@
}
}
}
- return isVisibleRequestedOrAdding();
+ return isVisibleRequestedOrAdding()
+ || (isVisible() && mActivityRecord != null && mActivityRecord.isVisible());
}
private final class DeadWindowEventReceiver extends InputEventReceiver {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 6aa6323..3cd4e5e 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1595,7 +1595,7 @@
if (!inputChannel.ok()) {
std::string message = inputChannel.error().message();
- message += StringPrintf(" Status=%d", inputChannel.error().code());
+ message += StringPrintf(" Status=%d", static_cast<int>(inputChannel.error().code()));
jniThrowRuntimeException(env, message.c_str());
return nullptr;
}
@@ -1629,7 +1629,7 @@
if (!inputChannel.ok()) {
std::string message = inputChannel.error().message();
- message += StringPrintf(" Status=%d", inputChannel.error().code());
+ message += StringPrintf(" Status=%d", static_cast<int>(inputChannel.error().code()));
jniThrowRuntimeException(env, message.c_str());
return nullptr;
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 826171a..be656e3 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -232,6 +232,11 @@
namespace {
// Returns true if location has lat/long information.
+bool hasLatLong(const GnssLocationAidl& location) {
+ return (location.gnssLocationFlags & GnssLocationAidl::HAS_LAT_LONG) != 0;
+}
+
+// Returns true if location has lat/long information.
bool hasLatLong(const GnssLocation_V1_0& location) {
return (static_cast<uint32_t>(location.gnssLocationFlags) &
GnssLocationFlags::HAS_LAT_LONG) != 0;
@@ -248,6 +253,36 @@
return value ? JNI_TRUE : JNI_FALSE;
}
+static GnssLocationAidl createGnssLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
+ jdouble longitudeDegrees, jdouble altitudeMeters,
+ jfloat speedMetersPerSec, jfloat bearingDegrees,
+ jfloat horizontalAccuracyMeters,
+ jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond,
+ jfloat bearingAccuracyDegrees, jlong timestamp,
+ jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+ jdouble elapsedRealtimeUncertaintyNanos) {
+ GnssLocationAidl location;
+ location.gnssLocationFlags = static_cast<int>(gnssLocationFlags);
+ location.latitudeDegrees = static_cast<double>(latitudeDegrees);
+ location.longitudeDegrees = static_cast<double>(longitudeDegrees);
+ location.altitudeMeters = static_cast<double>(altitudeMeters);
+ location.speedMetersPerSec = static_cast<double>(speedMetersPerSec);
+ location.bearingDegrees = static_cast<double>(bearingDegrees);
+ location.horizontalAccuracyMeters = static_cast<double>(horizontalAccuracyMeters);
+ location.verticalAccuracyMeters = static_cast<double>(verticalAccuracyMeters);
+ location.speedAccuracyMetersPerSecond = static_cast<double>(speedAccuracyMetersPerSecond);
+ location.bearingAccuracyDegrees = static_cast<double>(bearingAccuracyDegrees);
+ location.timestampMillis = static_cast<uint64_t>(timestamp);
+
+ location.elapsedRealtime.flags = static_cast<int>(elapsedRealtimeFlags);
+ location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
+ location.elapsedRealtime.timeUncertaintyNs =
+ static_cast<double>(elapsedRealtimeUncertaintyNanos);
+
+ return location;
+}
+
static GnssLocation_V1_0 createGnssLocation_V1_0(
jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
@@ -298,7 +333,8 @@
Return<void> gnssLocationCb(const GnssLocation_V1_0& location) override;
Return<void> gnssStatusCb(const IGnssCallback_V1_0::GnssStatusValue status) override;
Return<void> gnssSvStatusCb(const IGnssCallback_V1_0::GnssSvStatus& svStatus) override {
- return gnssSvStatusCbImpl(svStatus);
+ return gnssSvStatusCbImpl<IGnssCallback_V1_0::GnssSvStatus, IGnssCallback_V1_0::GnssSvInfo>(
+ svStatus);
}
Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override;
@@ -318,73 +354,67 @@
Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
Return<void> gnssLocationCb_2_0(const GnssLocation_V2_0& location) override;
Return<void> gnssSvStatusCb_2_0(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) override {
- return gnssSvStatusCbImpl(svInfoList);
+ return gnssSvStatusCbImpl<hidl_vec<IGnssCallback_V2_0::GnssSvInfo>,
+ IGnssCallback_V1_0::GnssSvInfo>(svInfoList);
}
// New in 2.1
Return<void> gnssSvStatusCb_2_1(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) override {
- return gnssSvStatusCbImpl(svInfoList);
+ return gnssSvStatusCbImpl<hidl_vec<IGnssCallback_V2_1::GnssSvInfo>,
+ IGnssCallback_V1_0::GnssSvInfo>(svInfoList);
}
Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override;
// TODO: Reconsider allocation cost vs threadsafety on these statics
static const char* sNmeaString;
static size_t sNmeaStringLength;
+
+ template <class T>
+ static Return<void> gnssLocationCbImpl(const T& location);
+
+ template <class T_list, class T_sv_info>
+ static Return<void> gnssSvStatusCbImpl(const T_list& svStatus);
+
private:
- template<class T>
- Return<void> gnssLocationCbImpl(const T& location);
-
- template<class T>
- Return<void> gnssSvStatusCbImpl(const T& svStatus);
-
- template<class T>
- uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) {
+ template <class T>
+ static uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) {
return 0;
}
- template<class T>
- double getBasebandCn0DbHz(const T& svStatus, size_t i) {
+ template <class T>
+ static double getBasebandCn0DbHz(const T& svStatus, size_t i) {
return 0.0;
}
- uint32_t getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
- return svStatus.numSvs;
- }
-
- uint32_t getGnssSvInfoListSize(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) {
+ template <class T>
+ static uint32_t getGnssSvInfoListSize(const T& svInfoList) {
return svInfoList.size();
}
- uint32_t getGnssSvInfoListSize(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) {
- return svInfoList.size();
+ static const IGnssCallbackAidl::GnssSvInfo& getGnssSvInfoOfIndex(
+ const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) {
+ return svInfoList[i];
}
- const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
+ static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
const IGnssCallback_V1_0::GnssSvStatus& svStatus, size_t i) {
return svStatus.gnssSvList.data()[i];
}
- const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
+ static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList, size_t i) {
return svInfoList[i].v1_0;
}
- const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
+ static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
return svInfoList[i].v2_0.v1_0;
}
- uint32_t getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus, size_t i) {
- return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation);
- }
-
- uint32_t getConstellationType(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList, size_t i) {
+ template <class T>
+ static uint32_t getConstellationType(const T& svInfoList, size_t i) {
return static_cast<uint32_t>(svInfoList[i].constellation);
}
-
- uint32_t getConstellationType(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
- return static_cast<uint32_t>(svInfoList[i].v2_0.constellation);
- }
};
Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
@@ -441,14 +471,43 @@
return SVID_FLAGS_HAS_BASEBAND_CN0;
}
+template <>
+uint32_t GnssCallback::getHasBasebandCn0DbHzFlag(
+ const std::vector<IGnssCallbackAidl::GnssSvInfo>& svStatus) {
+ return SVID_FLAGS_HAS_BASEBAND_CN0;
+}
+
+template <>
+double GnssCallback::getBasebandCn0DbHz(
+ const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) {
+ return svInfoList[i].basebandCN0DbHz;
+}
+
template<>
double GnssCallback::getBasebandCn0DbHz(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList,
size_t i) {
return svInfoList[i].basebandCN0DbHz;
}
-template<class T>
-Return<void> GnssCallback::gnssSvStatusCbImpl(const T& svStatus) {
+template <>
+uint32_t GnssCallback::getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
+ return svStatus.numSvs;
+}
+
+template <>
+uint32_t GnssCallback::getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus,
+ size_t i) {
+ return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation);
+}
+
+template <>
+uint32_t GnssCallback::getConstellationType(
+ const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
+ return static_cast<uint32_t>(svInfoList[i].v2_0.constellation);
+}
+
+template <class T_list, class T_sv_info>
+Return<void> GnssCallback::gnssSvStatusCbImpl(const T_list& svStatus) {
JNIEnv* env = getJniEnv();
uint32_t listSize = getGnssSvInfoListSize(svStatus);
@@ -476,7 +535,7 @@
CONSTELLATION_TYPE_SHIFT_WIDTH = 8
};
- const IGnssCallback_V1_0::GnssSvInfo& info = getGnssSvInfoOfIndex(svStatus, i);
+ const T_sv_info& info = getGnssSvInfoOfIndex(svStatus, i);
svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
(getConstellationType(svStatus, i) << CONSTELLATION_TYPE_SHIFT_WIDTH) |
static_cast<uint32_t>(info.svFlag);
@@ -586,6 +645,16 @@
class GnssCallbackAidl : public android::hardware::gnss::BnGnssCallback {
public:
Status gnssSetCapabilitiesCb(const int capabilities) override;
+ Status gnssStatusCb(const GnssStatusValue status) override;
+ Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override;
+ Status gnssLocationCb(const GnssLocationAidl& location) override;
+ Status gnssNmeaCb(const int64_t timestamp, const std::string& nmea) override;
+ Status gnssAcquireWakelockCb() override;
+ Status gnssReleaseWakelockCb() override;
+ Status gnssSetSystemInfoCb(const GnssSystemInfo& info) override;
+ Status gnssRequestTimeCb() override;
+ Status gnssRequestLocationCb(const bool independentFromGnss,
+ const bool isUserEmergency) override;
};
Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) {
@@ -596,6 +665,76 @@
return Status::ok();
}
+Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) {
+ GnssCallback::gnssSvStatusCbImpl<std::vector<GnssSvInfo>, GnssSvInfo>(svInfoList);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssLocationCb(const GnssLocationAidl& location) {
+ GnssCallback::gnssLocationCbImpl<GnssLocationAidl>(location);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) {
+ JNIEnv* env = getJniEnv();
+ /*
+ * The Java code will call back to read these values.
+ * We do this to avoid creating unnecessary String objects.
+ */
+ GnssCallback::sNmeaString = nmea.c_str();
+ GnssCallback::sNmeaStringLength = nmea.size();
+
+ env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssAcquireWakelockCb() {
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssReleaseWakelockCb() {
+ release_wake_lock(WAKE_LOCK_NAME);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssSetSystemInfoCb(const GnssSystemInfo& info) {
+ ALOGD("%s: yearOfHw=%d, name=%s\n", __func__, info.yearOfHw, info.name.c_str());
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw);
+ jstring jstringName = env->NewStringUTF(info.name.c_str());
+ env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
+ if (jstringName) {
+ env->DeleteLocalRef(jstringName);
+ }
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssRequestTimeCb() {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssRequestLocationCb(const bool independentFromGnss,
+ const bool isUserEmergency) {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss),
+ boolToJbool(isUserEmergency));
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
/*
* GnssPowerIndicationCallback class implements the callback methods for the IGnssPowerIndication
* interface.
@@ -797,7 +936,10 @@
static void android_location_gnss_hal_GnssNative_set_gps_service_handle() {
gnssHalAidl = waitForVintfService<IGnssAidl>();
if (gnssHalAidl != nullptr) {
- ALOGD("Successfully got GNSS AIDL handle.");
+ ALOGD("Successfully got GNSS AIDL handle. Version=%d.", gnssHalAidl->getInterfaceVersion());
+ if (gnssHalAidl->getInterfaceVersion() >= 2) {
+ return;
+ }
}
ALOGD("Trying IGnss_V2_1::getService()");
@@ -952,15 +1094,17 @@
// TODO: linkToDeath for AIDL HAL
- gnssHalDeathRecipient = new GnssDeathRecipient();
- hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to GnssHAL death: %s",
- linked.description().c_str());
- } else if (!linked) {
- ALOGW("Unable to link to GnssHal death notifications");
- } else {
- ALOGD("Link to death notification successful");
+ if (gnssHal != nullptr) {
+ gnssHalDeathRecipient = new GnssDeathRecipient();
+ hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to GnssHAL death: %s",
+ linked.description().c_str());
+ } else if (!linked) {
+ ALOGW("Unable to link to GnssHal death notifications");
+ } else {
+ ALOGD("Link to death notification successful");
+ }
}
if (gnssHalAidl != nullptr) {
@@ -1163,7 +1307,7 @@
gnssConfigurationIface =
std::make_unique<gnss::GnssConfiguration_V1_1>(gnssConfiguration);
}
- } else {
+ } else if (gnssHal != nullptr) {
auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration();
if (checkHidlReturn(gnssConfiguration,
"Unable to get a handle to GnssConfiguration_V1_0")) {
@@ -1229,7 +1373,7 @@
}
static jboolean android_location_gnss_hal_GnssNative_is_supported(JNIEnv* /* env */, jclass) {
- return (gnssHal != nullptr) ? JNI_TRUE : JNI_FALSE;
+ return (gnssHalAidl != nullptr || gnssHal != nullptr) ? JNI_TRUE : JNI_FALSE;
}
static jboolean android_location_GnssNetworkConnectivityHandler_is_agps_ril_supported(
@@ -1263,26 +1407,26 @@
return JNI_FALSE;
}
- Return<bool> result = false;
-
// Set top level IGnss.hal callback.
- sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallback();
- if (gnssHal_V2_1 != nullptr) {
- result = gnssHal_V2_1->setCallback_2_1(gnssCbIface);
- } else if (gnssHal_V2_0 != nullptr) {
- result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
- } else if (gnssHal_V1_1 != nullptr) {
- result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
- } else if (gnssHal != nullptr) {
- result = gnssHal->setCallback(gnssCbIface);
+ if (gnssHal != nullptr) {
+ Return<bool> result = false;
+ sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallback();
+ if (gnssHal_V2_1 != nullptr) {
+ result = gnssHal_V2_1->setCallback_2_1(gnssCbIface);
+ } else if (gnssHal_V2_0 != nullptr) {
+ result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
+ } else if (gnssHal_V1_1 != nullptr) {
+ result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
+ } else {
+ result = gnssHal->setCallback(gnssCbIface);
+ }
+ if (!checkHidlReturn(result, "IGnss setCallback() failed.")) {
+ return JNI_FALSE;
+ }
}
- if (!checkHidlReturn(result, "IGnss setCallback() failed.")) {
- return JNI_FALSE;
- }
-
- sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl();
if (gnssHalAidl != nullptr) {
+ sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl();
auto status = gnssHalAidl->setCallback(gnssCbIfaceAidl);
if (!checkAidlStatus(status, "IGnssAidl setCallback() failed.")) {
return JNI_FALSE;
@@ -1298,7 +1442,7 @@
}
} else if (gnssXtraIface != nullptr) {
sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback();
- result = gnssXtraIface->setCallback(gnssXtraCbIface);
+ auto result = gnssXtraIface->setCallback(gnssXtraCbIface);
if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
gnssXtraIface = nullptr;
} else {
@@ -1341,20 +1485,20 @@
if (gnssVisibilityControlIface != nullptr) {
sp<IGnssVisibilityControlCallback> gnssVisibilityControlCbIface =
new GnssVisibilityControlCallback();
- result = gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
+ auto result = gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
checkHidlReturn(result, "IGnssVisibilityControl setCallback() failed.");
}
// Set IMeasurementCorrections.hal callback.
if (gnssCorrectionsIface_V1_1 != nullptr) {
- sp<IMeasurementCorrectionsCallback> gnssCorrectionsIfaceCbIface =
- new MeasurementCorrectionsCallback();
- result = gnssCorrectionsIface_V1_1->setCallback(gnssCorrectionsIfaceCbIface);
- checkHidlReturn(result, "IMeasurementCorrections 1.1 setCallback() failed.");
+ sp<IMeasurementCorrectionsCallback> gnssCorrectionsIfaceCbIface =
+ new MeasurementCorrectionsCallback();
+ auto result = gnssCorrectionsIface_V1_1->setCallback(gnssCorrectionsIfaceCbIface);
+ checkHidlReturn(result, "IMeasurementCorrections 1.1 setCallback() failed.");
} else if (gnssCorrectionsIface_V1_0 != nullptr) {
sp<IMeasurementCorrectionsCallback> gnssCorrectionsIfaceCbIface =
new MeasurementCorrectionsCallback();
- result = gnssCorrectionsIface_V1_0->setCallback(gnssCorrectionsIfaceCbIface);
+ auto result = gnssCorrectionsIface_V1_0->setCallback(gnssCorrectionsIfaceCbIface);
checkHidlReturn(result, "IMeasurementCorrections 1.0 setCallback() failed.");
} else {
ALOGI("Unable to find IMeasurementCorrections.");
@@ -1388,6 +1532,15 @@
static jboolean android_location_gnss_hal_GnssNative_set_position_mode(
JNIEnv* /* env */, jclass, jint mode, jint recurrence, jint min_interval,
jint preferred_accuracy, jint preferred_time, jboolean low_power_mode) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->setPositionMode(static_cast<IGnssAidl::GnssPositionMode>(mode),
+ static_cast<IGnssAidl::GnssPositionRecurrence>(
+ recurrence),
+ min_interval, preferred_accuracy, preferred_time,
+ low_power_mode);
+ return checkAidlStatus(status, "IGnssAidl setPositionMode() failed.");
+ }
+
Return<bool> result = false;
if (gnssHal_V1_1 != nullptr) {
result = gnssHal_V1_1->setPositionMode_1_1(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
@@ -1408,6 +1561,11 @@
}
static jboolean android_location_gnss_hal_GnssNative_start(JNIEnv* /* env */, jclass) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->start();
+ return checkAidlStatus(status, "IGnssAidl start() failed.");
+ }
+
if (gnssHal == nullptr) {
return JNI_FALSE;
}
@@ -1417,6 +1575,11 @@
}
static jboolean android_location_gnss_hal_GnssNative_stop(JNIEnv* /* env */, jclass) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->stop();
+ return checkAidlStatus(status, "IGnssAidl stop() failed.");
+ }
+
if (gnssHal == nullptr) {
return JNI_FALSE;
}
@@ -1427,6 +1590,12 @@
static void android_location_gnss_hal_GnssNative_delete_aiding_data(JNIEnv* /* env */, jclass,
jint flags) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->deleteAidingData(static_cast<IGnssAidl::GnssAidingData>(flags));
+ checkAidlStatus(status, "IGnssAidl deleteAidingData() failed.");
+ return;
+ }
+
if (gnssHal == nullptr) {
return;
}
@@ -1490,10 +1659,15 @@
static void android_location_gnss_hal_GnssNative_inject_time(JNIEnv* /* env */, jclass, jlong time,
jlong timeReference,
jint uncertainty) {
- if (gnssHal == nullptr) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->injectTime(time, timeReference, uncertainty);
+ checkAidlStatus(status, "IGnssAidl injectTime() failed.");
return;
}
+ if (gnssHal == nullptr) {
+ return;
+ }
auto result = gnssHal->injectTime(time, timeReference, uncertainty);
checkHidlReturn(result, "IGnss injectTime() failed.");
}
@@ -1505,6 +1679,19 @@
jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp,
jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
jdouble elapsedRealtimeUncertaintyNanos) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ GnssLocationAidl location =
+ createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+ altitudeMeters, speedMetersPerSec, bearingDegrees,
+ horizontalAccuracyMeters, verticalAccuracyMeters,
+ speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+ elapsedRealtimeFlags, elapsedRealtimeNanos,
+ elapsedRealtimeUncertaintyNanos);
+ auto status = gnssHalAidl->injectBestLocation(location);
+ checkAidlStatus(status, "IGnssAidl injectBestLocation() failed.");
+ return;
+ }
+
if (gnssHal_V2_0 != nullptr) {
GnssLocation_V2_0 location = createGnssLocation_V2_0(
gnssLocationFlags,
@@ -1546,15 +1733,31 @@
ALOGE("IGnss injectBestLocation() is called but gnssHal_V1_1 is not available.");
}
-static void android_location_gnss_hal_GnssNative_inject_location(JNIEnv* /* env */, jclass,
- jdouble latitude,
- jdouble longitude,
- jfloat accuracy) {
- if (gnssHal == nullptr) {
+static void android_location_gnss_hal_GnssNative_inject_location(
+ JNIEnv* /* env */, jclass, jint gnssLocationFlags, jdouble latitudeDegrees,
+ jdouble longitudeDegrees, jdouble altitudeMeters, jfloat speedMetersPerSec,
+ jfloat bearingDegrees, jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp,
+ jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+ jdouble elapsedRealtimeUncertaintyNanos) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ GnssLocationAidl location =
+ createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+ altitudeMeters, speedMetersPerSec, bearingDegrees,
+ horizontalAccuracyMeters, verticalAccuracyMeters,
+ speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+ elapsedRealtimeFlags, elapsedRealtimeNanos,
+ elapsedRealtimeUncertaintyNanos);
+ auto status = gnssHalAidl->injectLocation(location);
+ checkAidlStatus(status, "IGnssAidl injectLocation() failed.");
return;
}
- auto result = gnssHal->injectLocation(latitude, longitude, accuracy);
+ if (gnssHal == nullptr) {
+ return;
+ }
+ auto result =
+ gnssHal->injectLocation(latitudeDegrees, longitudeDegrees, horizontalAccuracyMeters);
checkHidlReturn(result, "IGnss injectLocation() failed.");
}
@@ -1806,15 +2009,20 @@
}
static jboolean android_location_gnss_hal_GnssNative_start_measurement_collection(
- JNIEnv* /* env */, jclass, jboolean enableFullTracking, jboolean enableCorrVecOutputs) {
+ JNIEnv* /* env */, jclass, jboolean enableFullTracking, jboolean enableCorrVecOutputs,
+ jint intervalMs) {
if (gnssMeasurementIface == nullptr) {
ALOGE("%s: IGnssMeasurement interface not available.", __func__);
return JNI_FALSE;
}
+ hardware::gnss::IGnssMeasurementInterface::Options options;
+ options.enableFullTracking = enableFullTracking;
+ options.enableCorrVecOutputs = enableCorrVecOutputs;
+ options.intervalMs = intervalMs;
return gnssMeasurementIface->setCallback(std::make_unique<gnss::GnssMeasurementCallback>(
mCallbacksObj),
- enableFullTracking, enableCorrVecOutputs);
+ options);
}
static jboolean android_location_gnss_hal_GnssNative_stop_measurement_collection(JNIEnv* env,
@@ -2202,7 +2410,7 @@
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_time)},
{"native_inject_best_location", "(IDDDFFFFFFJIJD)V",
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_best_location)},
- {"native_inject_location", "(DDF)V",
+ {"native_inject_location", "(IDDDFFFFFFJIJD)V",
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_location)},
{"native_supports_psds", "()Z",
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_supports_psds)},
@@ -2269,7 +2477,7 @@
/* name, signature, funcPtr */
{"native_is_measurement_supported", "()Z",
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_is_measurement_supported)},
- {"native_start_measurement_collection", "(ZZ)Z",
+ {"native_start_measurement_collection", "(ZZI)Z",
reinterpret_cast<void*>(
android_location_gnss_hal_GnssNative_start_measurement_collection)},
{"native_stop_measurement_collection", "()Z",
diff --git a/services/core/jni/gnss/AGnss.cpp b/services/core/jni/gnss/AGnss.cpp
index 00403d6..091fffd 100644
--- a/services/core/jni/gnss/AGnss.cpp
+++ b/services/core/jni/gnss/AGnss.cpp
@@ -43,7 +43,7 @@
jboolean AGnss::dataConnOpen(JNIEnv* env, jlong networkHandle, jstring apn, jint apnIpType) {
ScopedJniString jniApn{env, apn};
- auto status = mIAGnss->dataConnOpen(networkHandle, String16(jniApn.c_str()),
+ auto status = mIAGnss->dataConnOpen(networkHandle, std::string(jniApn.c_str()),
static_cast<IAGnss::ApnIpType>(apnIpType));
return checkAidlStatus(status,
"IAGnssAidl dataConnOpen() failed. APN and its IP type not set.");
@@ -61,8 +61,8 @@
jboolean AGnss::setServer(JNIEnv* env, jint type, jstring hostname, jint port) {
ScopedJniString jniHostName{env, hostname};
- auto status =
- mIAGnss->setServer(static_cast<AGnssType>(type), String16(jniHostName.c_str()), port);
+ auto status = mIAGnss->setServer(static_cast<AGnssType>(type), std::string(jniHostName.c_str()),
+ port);
return checkAidlStatus(status, "IAGnssAidl setServer() failed. Host name and port not set.");
}
diff --git a/services/core/jni/gnss/GnssMeasurement.cpp b/services/core/jni/gnss/GnssMeasurement.cpp
index 663d839..9fbf259 100644
--- a/services/core/jni/gnss/GnssMeasurement.cpp
+++ b/services/core/jni/gnss/GnssMeasurement.cpp
@@ -50,9 +50,15 @@
: mIGnssMeasurement(iGnssMeasurement) {}
jboolean GnssMeasurement::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) {
- auto status = mIGnssMeasurement->setCallback(callback->getAidl(), enableFullTracking,
- enableCorrVecOutputs);
+ const IGnssMeasurementInterface::Options& options) {
+ if (mIGnssMeasurement->getInterfaceVersion() >= 2) {
+ auto status = mIGnssMeasurement->setCallbackWithOptions(callback->getAidl(), options);
+ if (checkAidlStatus(status, "IGnssMeasurement setCallbackWithOptions() failed.")) {
+ return true;
+ }
+ }
+ auto status = mIGnssMeasurement->setCallback(callback->getAidl(), options.enableFullTracking,
+ options.enableCorrVecOutputs);
return checkAidlStatus(status, "IGnssMeasurement setCallback() failed.");
}
@@ -67,13 +73,16 @@
: mIGnssMeasurement_V1_0(iGnssMeasurement) {}
jboolean GnssMeasurement_V1_0::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) {
- if (enableFullTracking == true) {
+ const IGnssMeasurementInterface::Options& options) {
+ if (options.enableFullTracking == true) {
ALOGW("Full tracking mode is not supported in 1.0 GNSS HAL.");
}
- if (enableCorrVecOutputs == true) {
+ if (options.enableCorrVecOutputs == true) {
ALOGW("Correlation vector output is not supported in 1.0 GNSS HAL.");
}
+ if (options.intervalMs > 1000) {
+ ALOGW("Measurement interval is not supported in 1.0 GNSS HAL.");
+ }
auto status = mIGnssMeasurement_V1_0->setCallback(callback->getHidl());
if (!checkHidlReturn(status, "IGnssMeasurement setCallback() failed.")) {
return JNI_FALSE;
@@ -93,11 +102,15 @@
: GnssMeasurement_V1_0{iGnssMeasurement}, mIGnssMeasurement_V1_1(iGnssMeasurement) {}
jboolean GnssMeasurement_V1_1::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) {
- if (enableCorrVecOutputs == true) {
+ const IGnssMeasurementInterface::Options& options) {
+ if (options.enableCorrVecOutputs == true) {
ALOGW("Correlation vector output is not supported in 1.1 GNSS HAL.");
}
- auto status = mIGnssMeasurement_V1_1->setCallback_1_1(callback->getHidl(), enableFullTracking);
+ if (options.intervalMs > 1000) {
+ ALOGW("Measurement interval is not supported in 1.0 GNSS HAL.");
+ }
+ auto status = mIGnssMeasurement_V1_1->setCallback_1_1(callback->getHidl(),
+ options.enableFullTracking);
if (!checkHidlReturn(status, "IGnssMeasurement setCallback_V1_1() failed.")) {
return JNI_FALSE;
}
@@ -111,11 +124,15 @@
: GnssMeasurement_V1_1{iGnssMeasurement}, mIGnssMeasurement_V2_0(iGnssMeasurement) {}
jboolean GnssMeasurement_V2_0::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) {
- if (enableCorrVecOutputs == true) {
+ const IGnssMeasurementInterface::Options& options) {
+ if (options.enableCorrVecOutputs == true) {
ALOGW("Correlation vector output is not supported in 2.0 GNSS HAL.");
}
- auto status = mIGnssMeasurement_V2_0->setCallback_2_0(callback->getHidl(), enableFullTracking);
+ if (options.intervalMs > 1000) {
+ ALOGW("Measurement interval is not supported in 1.0 GNSS HAL.");
+ }
+ auto status = mIGnssMeasurement_V2_0->setCallback_2_0(callback->getHidl(),
+ options.enableFullTracking);
if (!checkHidlReturn(status, "IGnssMeasurement setCallback_2_0() failed.")) {
return JNI_FALSE;
}
@@ -129,11 +146,15 @@
: GnssMeasurement_V2_0{iGnssMeasurement}, mIGnssMeasurement_V2_1(iGnssMeasurement) {}
jboolean GnssMeasurement_V2_1::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) {
- if (enableCorrVecOutputs == true) {
+ const IGnssMeasurementInterface::Options& options) {
+ if (options.enableCorrVecOutputs == true) {
ALOGW("Correlation vector output is not supported in 2.1 GNSS HAL.");
}
- auto status = mIGnssMeasurement_V2_1->setCallback_2_1(callback->getHidl(), enableFullTracking);
+ if (options.intervalMs > 1000) {
+ ALOGW("Measurement interval is not supported in 1.0 GNSS HAL.");
+ }
+ auto status = mIGnssMeasurement_V2_1->setCallback_2_1(callback->getHidl(),
+ options.enableFullTracking);
if (!checkHidlReturn(status, "IGnssMeasurement setCallback_2_1() failed.")) {
return JNI_FALSE;
}
diff --git a/services/core/jni/gnss/GnssMeasurement.h b/services/core/jni/gnss/GnssMeasurement.h
index f0752cd..7a95db8 100644
--- a/services/core/jni/gnss/GnssMeasurement.h
+++ b/services/core/jni/gnss/GnssMeasurement.h
@@ -37,16 +37,18 @@
class GnssMeasurementInterface {
public:
virtual ~GnssMeasurementInterface() {}
- virtual jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) = 0;
+ virtual jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) = 0;
virtual jboolean close() = 0;
};
class GnssMeasurement : public GnssMeasurementInterface {
public:
GnssMeasurement(const sp<android::hardware::gnss::IGnssMeasurementInterface>& iGnssMeasurement);
- jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) override;
+ jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) override;
jboolean close() override;
private:
@@ -57,8 +59,9 @@
public:
GnssMeasurement_V1_0(
const sp<android::hardware::gnss::V1_0::IGnssMeasurement>& iGnssMeasurement);
- jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) override;
+ jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) override;
jboolean close() override;
private:
@@ -69,8 +72,9 @@
public:
GnssMeasurement_V1_1(
const sp<android::hardware::gnss::V1_1::IGnssMeasurement>& iGnssMeasurement);
- jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) override;
+ jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) override;
private:
const sp<android::hardware::gnss::V1_1::IGnssMeasurement> mIGnssMeasurement_V1_1;
@@ -80,8 +84,9 @@
public:
GnssMeasurement_V2_0(
const sp<android::hardware::gnss::V2_0::IGnssMeasurement>& iGnssMeasurement);
- jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) override;
+ jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) override;
private:
const sp<android::hardware::gnss::V2_0::IGnssMeasurement> mIGnssMeasurement_V2_0;
@@ -91,8 +96,9 @@
public:
GnssMeasurement_V2_1(
const sp<android::hardware::gnss::V2_1::IGnssMeasurement>& iGnssMeasurement);
- jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) override;
+ jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) override;
private:
const sp<android::hardware::gnss::V2_1::IGnssMeasurement> mIGnssMeasurement_V2_1;
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 38e882e..334f503 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -301,6 +301,7 @@
whenever(isInstalled) { true }
whenever(isSuspended) { false }
whenever(isInstantApp) { false }
+ whenever(firstInstallTime) {0L}
}
})
val pkg2 = mockPkgState(PKG_TWO, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
@@ -548,7 +549,6 @@
whenever(getPkg()) { pkg }
whenever(packageName) { pkgName }
whenever(this.domainSetId) { domainSetId }
- whenever(firstInstallTime) { 0L }
whenever(getUserStateOrDefault(0)) { pkgUserState0() }
whenever(getUserStateOrDefault(1)) { pkgUserState1() }
whenever(userStates) {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index ad79c65..fb581d7 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -896,7 +896,6 @@
whenever(this.pkg) { pkg }
whenever(packageName) { pkgName }
whenever(this.domainSetId) { domainSetId }
- whenever(firstInstallTime) { 0L }
whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
whenever(getUserStateOrDefault(10)) { PackageUserStateInternal.DEFAULT }
whenever(userStates) {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 9fa1a235..728da49 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -147,7 +147,6 @@
whenever(this.pkg) { pkg }
whenever(packageName) { pkgName }
whenever(this.domainSetId) { domainSetId }
- whenever(firstInstallTime) { 0L }
whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
whenever(getUserStateOrDefault(1)) { PackageUserStateInternal.DEFAULT }
whenever(userStates) {
diff --git a/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml b/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml
index 48cee7e..eb741ca 100644
--- a/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml
+++ b/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="test_com.android.server">
<!-- APEX does not have classes.dex -->
- <application android:hasCode="false">
+ <application android:hasCode="false" android:testOnly="true">
<apex-system-service
android:name="com.android.server.testing.FakeApexSystemService"
android:path="/apex/test_com.android.server/javalib/FakeApexSystemService.jar"
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
index d6db1b2..c46884f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
@@ -16,57 +16,28 @@
package com.android.server.communal;
-import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-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.ActivityInterceptorCallback.COMMUNAL_MODE_ORDERED_ID;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import android.Manifest;
-import android.annotation.Nullable;
-import android.app.KeyguardManager;
import android.app.communal.ICommunalManager;
-import android.app.compat.CompatChanges;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.net.Uri;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
-import android.service.dreams.DreamManagerInternal;
-import android.test.mock.MockContentResolver;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.wm.ActivityInterceptorCallback;
-import com.android.server.wm.ActivityTaskManagerInternal;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.quality.Strictness;
@@ -81,25 +52,9 @@
@SmallTest
@Presubmit
public class CommunalManagerServiceTest {
- private static final int TEST_USER_ID = 1;
- private static final int TEST_REAL_CALLING_UID = 2;
- private static final int TEST_REAL_CALLING_PID = 3;
- private static final String TEST_CALLING_PACKAGE = "com.test.caller";
- private static final String TEST_PACKAGE_NAME = "com.test.package";
-
private MockitoSession mMockingSession;
private CommunalManagerService mService;
- @Mock
- private ActivityTaskManagerInternal mAtmInternal;
- @Mock
- private KeyguardManager mKeyguardManager;
- @Mock
- private DreamManagerInternal mDreamManagerInternal;
-
- private ActivityInterceptorCallback mActivityInterceptorCallback;
- private BroadcastReceiver mPackageReceiver;
- private ActivityInfo mAInfo;
private ICommunalManager mBinder;
private ContextWrapper mContextSpy;
@@ -107,19 +62,10 @@
public final void setUp() {
mMockingSession = mockitoSession()
.initMocks(this)
- .spyStatic(CommunalManagerService.class)
- .mockStatic(CompatChanges.class)
.strictness(Strictness.WARN)
.startMocking();
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
- MockContentResolver cr = new MockContentResolver(mContextSpy);
- cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- when(mContextSpy.getContentResolver()).thenReturn(cr);
-
- when(mContextSpy.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
- addLocalServiceMock(ActivityTaskManagerInternal.class, mAtmInternal);
- addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternal);
doNothing().when(mContextSpy).enforceCallingPermission(
eq(Manifest.permission.WRITE_COMMUNAL_STATE), anyString());
@@ -127,83 +73,16 @@
eq(Manifest.permission.READ_COMMUNAL_STATE), anyString());
mService = new CommunalManagerService(mContextSpy);
- mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
-
- ArgumentCaptor<ActivityInterceptorCallback> activityInterceptorCaptor =
- ArgumentCaptor.forClass(ActivityInterceptorCallback.class);
- verify(mAtmInternal).registerActivityStartInterceptor(eq(COMMUNAL_MODE_ORDERED_ID),
- activityInterceptorCaptor.capture());
- mActivityInterceptorCallback = activityInterceptorCaptor.getValue();
-
- ArgumentCaptor<BroadcastReceiver> packageReceiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mContextSpy).registerReceiverAsUser(packageReceiverCaptor.capture(),
- eq(UserHandle.SYSTEM), any(), any(), any());
- mPackageReceiver = packageReceiverCaptor.getValue();
-
mBinder = mService.getBinderServiceInstance();
-
- mAInfo = new ActivityInfo();
- mAInfo.applicationInfo = new ApplicationInfo();
- mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
}
@After
public void tearDown() {
- FakeSettingsProvider.clearSettingsProvider();
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
}
- /**
- * Creates a mock and registers it to {@link LocalServices}.
- */
- private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
- LocalServices.removeServiceForTest(clazz);
- LocalServices.addService(clazz, mock);
- }
-
- private ActivityInterceptorCallback.ActivityInterceptorInfo buildActivityInfo(Intent intent) {
- return new ActivityInterceptorCallback.ActivityInterceptorInfo(
- TEST_REAL_CALLING_UID,
- TEST_REAL_CALLING_PID,
- TEST_USER_ID,
- TEST_CALLING_PACKAGE,
- "featureId",
- intent,
- null,
- mAInfo,
- "resolvedType",
- TEST_REAL_CALLING_PID,
- TEST_REAL_CALLING_UID,
- null);
- }
-
- private void allowPackages(String packages) {
- Settings.Secure.putStringForUser(mContextSpy.getContentResolver(),
- Settings.Secure.COMMUNAL_MODE_PACKAGES, packages, UserHandle.USER_SYSTEM);
- }
-
- private String getAllowedPackages() {
- return Settings.Secure.getStringForUser(mContextSpy.getContentResolver(),
- Settings.Secure.COMMUNAL_MODE_PACKAGES, UserHandle.USER_SYSTEM);
- }
-
- private void assertDoesIntercept() {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
- assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNotNull();
- }
-
- private void assertDoesNotIntercept() {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
- assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
- }
-
- private Intent createPackageIntent(String packageName, @Nullable String action) {
- return new Intent(action, Uri.parse("package:" + packageName));
- }
-
@Test
public void testIsCommunalMode_isTrue() throws RemoteException {
mBinder.setCommunalViewShowing(true);
@@ -215,149 +94,4 @@
mBinder.setCommunalViewShowing(false);
assertThat(mBinder.isCommunalMode()).isFalse();
}
-
- @Test
- public void testIntercept_unlocked_communalOff_appNotEnabled_showWhenLockedOff() {
- when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
- mAInfo.flags = 0;
- assertDoesNotIntercept();
- }
-
- @Test
- public void testIntercept_unlocked_communalOn_appNotEnabled_showWhenLockedOff()
- throws RemoteException {
- mBinder.setCommunalViewShowing(true);
- when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
- mAInfo.flags = 0;
- assertDoesNotIntercept();
- }
-
- @Test
- public void testIntercept_locked_communalOff_appNotEnabled_showWhenLockedOff() {
- when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
- mAInfo.flags = 0;
- assertDoesNotIntercept();
- }
-
- @Test
- public void testIntercept_locked_communalOn_appNotEnabled_showWhenLockedOff_allowlistEnabled()
- throws RemoteException {
- mBinder.setCommunalViewShowing(true);
- when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
- when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
- UserHandle.SYSTEM)).thenReturn(true);
- mAInfo.flags = 0;
- assertDoesIntercept();
- }
-
- @Test
- public void testIntercept_locked_communalOn_appNotEnabled_showWhenLockedOn_allowlistEnabled()
- throws RemoteException {
- mBinder.setCommunalViewShowing(true);
- when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
- when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
- UserHandle.SYSTEM)).thenReturn(true);
- mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
-
- allowPackages("package1,package2");
- assertDoesIntercept();
- }
-
- @Test
- public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOff_allowlistEnabled()
- throws RemoteException {
- mBinder.setCommunalViewShowing(true);
- when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
- when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
- UserHandle.SYSTEM)).thenReturn(true);
- mAInfo.flags = 0;
-
- allowPackages(TEST_PACKAGE_NAME);
- assertDoesIntercept();
- }
-
- @Test
- public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOn_allowlistEnabled()
- throws RemoteException {
- mBinder.setCommunalViewShowing(true);
- when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
- when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
- UserHandle.SYSTEM)).thenReturn(true);
-
- mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
-
- allowPackages(TEST_PACKAGE_NAME);
- assertDoesNotIntercept();
- }
-
- @Test
- public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOn_allowlistDisabled()
- throws RemoteException {
- mBinder.setCommunalViewShowing(true);
- when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
- when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
- UserHandle.SYSTEM)).thenReturn(false);
-
- mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
-
- allowPackages(TEST_PACKAGE_NAME);
- assertDoesIntercept();
- }
-
- @Test
- public void testIntercept_locked_communalOn_dream() throws RemoteException {
- mBinder.setCommunalViewShowing(true);
- when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
- when(mDreamManagerInternal.getActiveDreamComponent(false)).thenReturn(
- new ComponentName(TEST_PACKAGE_NAME, "SomeClass"));
-
- allowPackages(TEST_PACKAGE_NAME);
- assertDoesNotIntercept();
- }
-
- @Test
- public void testUpdateSettings_packageUninstalled() {
- allowPackages("package1,package2");
- assertThat(getAllowedPackages()).isEqualTo("package1,package2");
-
- mPackageReceiver.onReceive(mContextSpy,
- createPackageIntent("package1", ACTION_PACKAGE_REMOVED));
-
- assertThat(getAllowedPackages()).isEqualTo("package2");
- }
-
- @Test
- public void testUpdateSettings_nullAction_doesNothing() {
- allowPackages("package1,package2");
- assertThat(getAllowedPackages()).isEqualTo("package1,package2");
-
- mPackageReceiver.onReceive(mContextSpy,
- createPackageIntent("package1", null));
-
- assertThat(getAllowedPackages()).isEqualTo("package1,package2");
- }
-
- @Test
- public void testUpdateSettings_invalidPackage_doesNothing() {
- allowPackages("package1,package2");
- assertThat(getAllowedPackages()).isEqualTo("package1,package2");
-
- mPackageReceiver.onReceive(mContextSpy,
- createPackageIntent("package3", ACTION_PACKAGE_REMOVED));
-
- assertThat(getAllowedPackages()).isEqualTo("package1,package2");
- }
-
- @Test
- public void testUpdateSettings_onBoot() {
- allowPackages("package1,package2");
- assertThat(getAllowedPackages()).isEqualTo("package1,package2");
-
- when(CommunalManagerService.isPackageInstalled(eq("package1"), any())).thenReturn(true);
- when(CommunalManagerService.isPackageInstalled(eq("package2"), any())).thenReturn(false);
-
- mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
-
- assertThat(getAllowedPackages()).isEqualTo("package1");
- }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
index e0c8b09..93a2d31 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
@@ -234,6 +234,7 @@
private boolean mMeasurementCollectionStarted = false;
private boolean mMeasurementCollectionFullTracking = false;
private boolean mMeasurementCollectionCorrVecOutputsEnabled = false;
+ private int mMeasurementCollectionIntervalMillis = 0;
private GnssHalPositionMode mPositionMode = new GnssHalPositionMode();
private GnssHalBatchingMode mBatchingMode = new GnssHalBatchingMode();
private final ArrayList<Location> mBatchedLocations = new ArrayList<>();
@@ -428,11 +429,15 @@
}
@Override
- protected void injectLocation(double latitude, double longitude, float accuracy) {
+ protected void injectLocation(@GnssLocationFlags int gnssLocationFlags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float horizontalAccuracy,
+ float verticalAccuracy, float speedAccuracy, float bearingAccuracy, long timestamp,
+ @GnssRealtimeFlags int elapsedRealtimeFlags, long elapsedRealtimeNanos,
+ double elapsedRealtimeUncertaintyNanos) {
mState.mInjectedLocation = new Location("injected");
mState.mInjectedLocation.setLatitude(latitude);
mState.mInjectedLocation.setLongitude(longitude);
- mState.mInjectedLocation.setAccuracy(accuracy);
+ mState.mInjectedLocation.setAccuracy(horizontalAccuracy);
}
@Override
@@ -523,10 +528,11 @@
@Override
protected boolean startMeasurementCollection(boolean enableFullTracking,
- boolean enableCorrVecOutputs) {
+ boolean enableCorrVecOutputs, int intervalMillis) {
mState.mMeasurementCollectionStarted = true;
mState.mMeasurementCollectionFullTracking = enableFullTracking;
mState.mMeasurementCollectionCorrVecOutputsEnabled = enableCorrVecOutputs;
+ mState.mMeasurementCollectionIntervalMillis = intervalMillis;
return true;
}
@@ -535,6 +541,7 @@
mState.mMeasurementCollectionStarted = false;
mState.mMeasurementCollectionFullTracking = false;
mState.mMeasurementCollectionCorrVecOutputsEnabled = false;
+ mState.mMeasurementCollectionIntervalMillis = 0;
return true;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 663bb2b..f2415b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -41,7 +41,7 @@
import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
import android.content.pm.StagedApexInfo;
import android.os.SystemProperties;
import android.os.storage.IStorageManager;
@@ -774,7 +774,7 @@
private boolean mIsReady = false;
private boolean mIsApplied = false;
private boolean mIsFailed = false;
- private @StagedSessionErrorCode int mErrorCode = -1;
+ private @SessionErrorCode int mErrorCode = -1;
private String mErrorMessage;
private boolean mIsDestroyed = false;
private int mParentSessionId = -1;
@@ -827,7 +827,7 @@
return this;
}
- private @StagedSessionErrorCode int getErrorCode() {
+ private @SessionErrorCode int getErrorCode() {
return mErrorCode;
}
@@ -939,7 +939,7 @@
}
@Override
- public void setSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
+ public void setSessionFailed(@SessionErrorCode int errorCode, String errorMessage) {
Preconditions.checkState(!mIsApplied, "Already marked as applied");
mIsFailed = true;
mErrorCode = errorCode;
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index e756124..202a54d 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -57,6 +57,7 @@
<uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
<uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+ <uses-permission android:name="android.permission.STATUS_BAR"/>
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/>
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER"/>
<uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index c9ae11a..0054fc3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -125,6 +125,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
FakeSettingsProvider.clearSettingsProvider();
+ final Object globalLock = new Object();
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
@@ -139,14 +140,14 @@
CURRENT_USER_ID);
mScaleProvider = spy(new MagnificationScaleProvider(mContext));
mWindowMagnificationManager = Mockito.spy(
- new WindowMagnificationManager(mContext, CURRENT_USER_ID,
+ new WindowMagnificationManager(mContext, globalLock,
mock(WindowMagnificationManager.Callback.class), mTraceManager,
mScaleProvider));
mMockConnection = new MockWindowMagnificationConnection(true);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber(
mScreenMagnificationController);
- mMagnificationController = new MagnificationController(mService, new Object(), mContext,
+ mMagnificationController = new MagnificationController(mService, globalLock, mContext,
mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider);
mMagnificationController.setMagnificationCapabilities(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index b807c11..e9f0bd9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -85,7 +85,7 @@
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0,
+ mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
mock(WindowMagnificationManager.Callback.class), mMockTrace,
new MagnificationScaleProvider(mContext));
mMockConnection = new MockWindowMagnificationConnection();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 8b7a191..a62c0d5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -94,7 +94,7 @@
LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
mResolver = new MockContentResolver();
mMockConnection = new MockWindowMagnificationConnection();
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID,
+ mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
when(mContext.getContentResolver()).thenReturn(mResolver);
diff --git a/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
index d5a28f6..d2ea9c4 100644
--- a/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
@@ -15,7 +15,7 @@
*/
package com.android.server.autofill;
-import static com.android.server.autofill.AutofillManagerService.getWhitelistedCompatModePackages;
+import static com.android.server.autofill.AutofillManagerService.getAllowedCompatModePackages;
import static com.google.common.truth.Truth.assertThat;
@@ -29,54 +29,54 @@
public class AutofillManagerServiceTest {
@Test
- public void testGetWhitelistedCompatModePackages_null() {
- assertThat(getWhitelistedCompatModePackages(null)).isNull();
+ public void testGetAllowedCompatModePackages_null() {
+ assertThat(getAllowedCompatModePackages(null)).isNull();
}
@Test
- public void testGetWhitelistedCompatModePackages_empty() {
- assertThat(getWhitelistedCompatModePackages("")).isNull();
+ public void testGetAllowedCompatModePackages_empty() {
+ assertThat(getAllowedCompatModePackages("")).isNull();
}
@Test
- public void testGetWhitelistedCompatModePackages_onePackageNoUrls() {
- assertThat(getWhitelistedCompatModePackages("one_is_the_loniest_package"))
+ public void testGetAllowedCompatModePackages_onePackageNoUrls() {
+ assertThat(getAllowedCompatModePackages("one_is_the_loniest_package"))
.containsExactly("one_is_the_loniest_package", null);
}
@Test
- public void testGetWhitelistedCompatModePackages_onePackageMissingEndDelimiter() {
- assertThat(getWhitelistedCompatModePackages("one_is_the_loniest_package[")).isEmpty();
+ public void testGetAllowedCompatModePackages_onePackageMissingEndDelimiter() {
+ assertThat(getAllowedCompatModePackages("one_is_the_loniest_package[")).isEmpty();
}
@Test
- public void testGetWhitelistedCompatModePackages_onePackageOneUrl() {
+ public void testGetAllowedCompatModePackages_onePackageOneUrl() {
final Map<String, String[]> result =
- getWhitelistedCompatModePackages("one_is_the_loniest_package[url]");
+ getAllowedCompatModePackages("one_is_the_loniest_package[url]");
assertThat(result).hasSize(1);
assertThat(result.get("one_is_the_loniest_package")).asList().containsExactly("url");
}
@Test
- public void testGetWhitelistedCompatModePackages_onePackageMultipleUrls() {
+ public void testGetAllowedCompatModePackages_onePackageMultipleUrls() {
final Map<String, String[]> result =
- getWhitelistedCompatModePackages("one_is_the_loniest_package[4,5,8,15,16,23,42]");
+ getAllowedCompatModePackages("one_is_the_loniest_package[4,5,8,15,16,23,42]");
assertThat(result).hasSize(1);
assertThat(result.get("one_is_the_loniest_package")).asList()
.containsExactly("4", "5", "8", "15", "16", "23", "42");
}
@Test
- public void testGetWhitelistedCompatModePackages_multiplePackagesOneInvalid() {
- final Map<String, String[]> result = getWhitelistedCompatModePackages("one:two[");
+ public void testGetAllowedCompatModePackages_multiplePackagesOneInvalid() {
+ final Map<String, String[]> result = getAllowedCompatModePackages("one:two[");
assertThat(result).hasSize(1);
assertThat(result.get("one")).isNull();
}
@Test
- public void testGetWhitelistedCompatModePackages_multiplePackagesMultipleUrls() {
+ public void testGetAllowedCompatModePackages_multiplePackagesMultipleUrls() {
final Map<String, String[]> result =
- getWhitelistedCompatModePackages("p1[p1u1]:p2:p3[p3u1,p3u2]");
+ getAllowedCompatModePackages("p1[p1u1]:p2:p3[p3u1,p3u2]");
assertThat(result).hasSize(3);
assertThat(result.get("p1")).asList().containsExactly("p1u1");
assertThat(result.get("p2")).isNull();
@@ -84,9 +84,9 @@
}
@Test
- public void testGetWhitelistedCompatModePackages_threePackagesOneInvalid() {
+ public void testGetAllowedCompatModePackages_threePackagesOneInvalid() {
final Map<String, String[]> result =
- getWhitelistedCompatModePackages("p1[p1u1]:p2[:p3[p3u1,p3u2]");
+ getAllowedCompatModePackages("p1[p1u1]:p2[:p3[p3u1,p3u2]");
assertThat(result).hasSize(2);
assertThat(result.get("p1")).asList().containsExactly("p1u1");
assertThat(result.get("p3")).asList().containsExactly("p3u1", "p3u2");
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index 6c4ae6f..62a2b1b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -306,12 +306,12 @@
assertEquals(expected.stageCid, actual.stageCid);
assertEquals(expected.isPrepared(), actual.isPrepared());
assertEquals(expected.isStaged(), actual.isStaged());
- assertEquals(expected.isStagedSessionApplied(), actual.isStagedSessionApplied());
- assertEquals(expected.isStagedSessionFailed(), actual.isStagedSessionFailed());
- assertEquals(expected.isStagedSessionReady(), actual.isStagedSessionReady());
- assertEquals(expected.getStagedSessionErrorCode(), actual.getStagedSessionErrorCode());
- assertEquals(expected.getStagedSessionErrorMessage(),
- actual.getStagedSessionErrorMessage());
+ assertEquals(expected.isSessionApplied(), actual.isSessionApplied());
+ assertEquals(expected.isSessionFailed(), actual.isSessionFailed());
+ assertEquals(expected.isSessionReady(), actual.isSessionReady());
+ assertEquals(expected.getSessionErrorCode(), actual.getSessionErrorCode());
+ assertEquals(expected.getSessionErrorMessage(),
+ actual.getSessionErrorMessage());
assertEquals(expected.isPrepared(), actual.isPrepared());
assertEquals(expected.isCommitted(), actual.isCommitted());
assertEquals(expected.createdMillis, actual.createdMillis);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 1b898fa..9d67240 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -48,6 +48,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
@@ -109,6 +110,8 @@
@Mock
DomainVerificationManagerInternal mDomainVerificationManager;
+ final ArrayMap<String, Long> mOrigFirstInstallTimes = new ArrayMap<>();
+
@Before
public void initializeMocks() {
MockitoAnnotations.initMocks(this);
@@ -208,14 +211,14 @@
new WatchableTester(settingsUnderTest, "noSuspendingPackage");
watcher.register();
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
- settingsUnderTest.readPackageRestrictionsLPr(0);
+ settingsUnderTest.readPackageRestrictionsLPr(0, mOrigFirstInstallTimes);
watcher.verifyChangeReported("put package 1");
// Collect a snapshot at the midway point (package 2 has not been added)
final Settings snapshot = settingsUnderTest.snapshot();
watcher.verifyNoChangeReported("snapshot");
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
watcher.verifyChangeReported("put package 2");
- settingsUnderTest.readPackageRestrictionsLPr(0);
+ settingsUnderTest.readPackageRestrictionsLPr(0, mOrigFirstInstallTimes);
PackageSetting ps1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1);
PackageUserStateInternal packageUserState1 = ps1.readUserState(0);
@@ -251,7 +254,7 @@
watcher.register();
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
watcher.verifyChangeReported("put package 1");
- settingsUnderTest.readPackageRestrictionsLPr(0);
+ settingsUnderTest.readPackageRestrictionsLPr(0, mOrigFirstInstallTimes);
watcher.verifyChangeReported("readPackageRestrictions");
final PackageSetting ps1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1);
@@ -338,7 +341,7 @@
settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3));
watcher.verifyChangeReported("put package 3");
// now read and verify
- settingsUnderTest.readPackageRestrictionsLPr(0);
+ settingsUnderTest.readPackageRestrictionsLPr(0, mOrigFirstInstallTimes);
watcher.verifyChangeReported("readPackageRestrictions");
final PackageUserStateInternal readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1)
.readUserState(0);
@@ -418,7 +421,7 @@
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3));
// now read and verify
- settingsUnderTest.readPackageRestrictionsLPr(0);
+ settingsUnderTest.readPackageRestrictionsLPr(0, mOrigFirstInstallTimes);
final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1)
.readUserState(0);
assertThat(readPus1.getDistractionFlags(), is(distractionFlags1));
@@ -1127,7 +1130,6 @@
assertSame(origPkgSetting.getCpuAbiOverride(), testPkgSetting.getCpuAbiOverride());
assertThat(origPkgSetting.getCpuAbiOverride(), is(testPkgSetting.getCpuAbiOverride()));
assertThat(origPkgSetting.getDomainSetId(), is(testPkgSetting.getDomainSetId()));
- assertThat(origPkgSetting.getFirstInstallTime(), is(testPkgSetting.getFirstInstallTime()));
assertSame(origPkgSetting.getInstallSource(), testPkgSetting.getInstallSource());
assertThat(origPkgSetting.isInstallPermissionsFixed(),
is(testPkgSetting.isInstallPermissionsFixed()));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index 0708be2..78bcf0c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -147,6 +147,11 @@
// Verifies pushDynamicShortcuts further persists shortcuts into AppSearch without
// removing previous shortcuts when max number of shortcuts is reached.
mManager.pushDynamicShortcut(makeShortcut("s6"));
+ // Increasing the max number of shortcuts since number of results per page in AppSearch
+ // is set to match the former.
+ mService.updateConfigurationLocked(
+ ShortcutService.ConfigConstants.KEY_MAX_SHORTCUTS + "=10,"
+ + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
shortcuts = getAllPersistedShortcuts();
assertNotNull(shortcuts);
assertEquals(6, shortcuts.size());
@@ -281,7 +286,7 @@
private List<ShortcutInfo> getAllPersistedShortcuts() {
try {
- SystemClock.sleep(500);
+ SystemClock.sleep(5000);
final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
getPersistedShortcut(future);
return future.get(10, TimeUnit.SECONDS);
diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
index c293b5e..d164d2a 100644
--- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
@@ -575,6 +576,30 @@
}
}
+ @Test
+ public void testSetNavBarModeOverride_setsOverrideModeKids() {
+ int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+ mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids);
+
+ assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride());
+ }
+
+ @Test
+ public void testSetNavBarModeOverride_setsOverrideModeNone() {
+ int navBarModeOverrideNone = StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE;
+ mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideNone);
+
+ assertEquals(navBarModeOverrideNone, mStatusBarManagerService.getNavBarModeOverride());
+ }
+
+ @Test
+ public void testSetNavBarModeOverride_invalidInputThrowsError() {
+ int navBarModeOverrideInvalid = -1;
+
+ assertThrows(UnsupportedOperationException.class,
+ () -> mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideInvalid));
+ }
+
private void mockUidCheck() {
mockUidCheck(TEST_PACKAGE);
}
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index add4cda..eeaf781 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -357,13 +357,13 @@
/**
* Tests that readPermissions works correctly for a library using the new
- * {@code updatable-library} tag.
+ * {@code apex-library} tag.
*/
@Test
public void readPermissions_allowLibs_parsesUpdatableLibrary() throws IOException {
String contents =
"<permissions>\n"
- + " <updatable-library \n"
+ + " <apex-library \n"
+ " name=\"foo\"\n"
+ " file=\"" + mFooJar + "\"\n"
+ " on-bootclasspath-before=\"10\"\n"
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 5062706..0d67946 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -678,6 +678,66 @@
opening, closing, false /* visible */));
}
+ @Test
+ public void testGetAnimationTargets_embeddedTask() {
+ // [DisplayContent] -+- [Task1] - [ActivityRecord1] (opening, invisible)
+ // +- [Task2] (embedded) - [ActivityRecord2] (opening, invisible)
+ final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
+ activity1.setVisible(false);
+ activity1.mVisibleRequested = true;
+
+ final Task task2 = createTask(mDisplayContent);
+ task2.mRemoveWithTaskOrganizer = true;
+ final ActivityRecord activity2 = createActivityRecord(task2);
+ activity2.setVisible(false);
+ activity2.mVisibleRequested = true;
+
+ final ArraySet<ActivityRecord> opening = new ArraySet<>();
+ opening.add(activity1);
+ opening.add(activity2);
+ final ArraySet<ActivityRecord> closing = new ArraySet<>();
+
+ // No animation on the embedded task.
+ assertEquals(
+ new ArraySet<>(new WindowContainer[]{activity1.getTask()}),
+ AppTransitionController.getAnimationTargets(
+ opening, closing, true /* visible */));
+ assertEquals(
+ new ArraySet<>(),
+ AppTransitionController.getAnimationTargets(
+ opening, closing, false /* visible */));
+ }
+
+
+ @Test
+ public void testGetAnimationTargets_activityInEmbeddedTask() {
+ // [DisplayContent] - [Task] (embedded)-+- [ActivityRecord1] (opening, invisible)
+ // +- [ActivityRecord2] (closing, visible)
+ final Task task = createTask(mDisplayContent);
+ task.mRemoveWithTaskOrganizer = true;
+
+ final ActivityRecord activity1 = createActivityRecord(task);
+ activity1.setVisible(false);
+ activity1.mVisibleRequested = true;
+ final ActivityRecord activity2 = createActivityRecord(task);
+
+ final ArraySet<ActivityRecord> opening = new ArraySet<>();
+ opening.add(activity1);
+ final ArraySet<ActivityRecord> closing = new ArraySet<>();
+ closing.add(activity2);
+
+ // Even though embedded task itself doesn't animate, activities in an embedded task
+ // animate.
+ assertEquals(
+ new ArraySet<>(new WindowContainer[]{activity1}),
+ AppTransitionController.getAnimationTargets(
+ opening, closing, true /* visible */));
+ assertEquals(
+ new ArraySet<>(new WindowContainer[]{activity2}),
+ AppTransitionController.getAnimationTargets(
+ opening, closing, false /* visible */));
+ }
+
static class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
@Override
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index a1d0eb8..790b154 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -25,11 +25,10 @@
import static com.android.server.wm.utils.CommonUtils.runWithShellPermissionIdentity;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import android.app.Activity;
+import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -57,7 +56,6 @@
import androidx.test.filters.MediumTest;
import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
@@ -73,49 +71,42 @@
@MediumTest
public class TaskStackChangedListenerTest {
- private static final int VIRTUAL_DISPLAY_WIDTH = 800;
- private static final int VIRTUAL_DISPLAY_HEIGHT = 600;
- private static final int VIRTUAL_DISPLAY_DENSITY = 160;
-
private ITaskStackListener mTaskStackListener;
- private DisplayManager mDisplayManager;
private VirtualDisplay mVirtualDisplay;
+ private ImageReader mImageReader;
private static final int WAIT_TIMEOUT_MS = 5000;
private static final Object sLock = new Object();
- @Before
- public void setUp() {
- mDisplayManager = getInstrumentation().getContext().getSystemService(
- DisplayManager.class);
- mVirtualDisplay = createVirtualDisplay(
- getClass().getSimpleName() + "_virtualDisplay",
- VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT, VIRTUAL_DISPLAY_DENSITY);
- }
-
@After
public void tearDown() throws Exception {
- ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
- mTaskStackListener = null;
- mVirtualDisplay.release();
+ if (mTaskStackListener != null) {
+ ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
+ }
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.release();
+ mImageReader.close();
+ }
}
- private VirtualDisplay createVirtualDisplay(String name, int width, int height, int density) {
- VirtualDisplay virtualDisplay = null;
- try (ImageReader reader = ImageReader.newInstance(width, height,
- /* format= */ PixelFormat.RGBA_8888, /* maxImages= */ 2)) {
- int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
- | VIRTUAL_DISPLAY_FLAG_PUBLIC;
- virtualDisplay = mDisplayManager.createVirtualDisplay(
- name, width, height, density, reader.getSurface(), flags);
- virtualDisplay.setSurface(reader.getSurface());
- }
- assertTrue("display id must be unique",
- virtualDisplay.getDisplay().getDisplayId() != Display.DEFAULT_DISPLAY);
+ private VirtualDisplay createVirtualDisplay() {
+ final int width = 800;
+ final int height = 600;
+ final int density = 160;
+ final DisplayManager displayManager = getInstrumentation().getContext().getSystemService(
+ DisplayManager.class);
+ mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
+ 2 /* maxImages */);
+ final int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ | VIRTUAL_DISPLAY_FLAG_PUBLIC;
+ final String name = getClass().getSimpleName() + "_VirtualDisplay";
+ mVirtualDisplay = displayManager.createVirtualDisplay(name, width, height, density,
+ mImageReader.getSurface(), flags);
+ mVirtualDisplay.setSurface(mImageReader.getSurface());
assertNotNull("display must be registered",
- Arrays.asList(mDisplayManager.getDisplays()).stream().filter(
+ Arrays.stream(displayManager.getDisplays()).filter(
d -> d.getName().equals(name)).findAny());
- return virtualDisplay;
+ return mVirtualDisplay;
}
@Test
@@ -163,11 +154,10 @@
mTaskId = taskId;
}
@Override
- public void onTaskDescriptionChanged(int taskId, TaskDescription td)
- throws RemoteException {
- if (mTaskId == taskId && !TextUtils.isEmpty(td.getLabel())) {
- params[0] = taskId;
- params[1] = td;
+ public void onTaskDescriptionChanged(RunningTaskInfo info) {
+ if (mTaskId == info.taskId && !TextUtils.isEmpty(info.taskDescription.getLabel())) {
+ params[0] = info.taskId;
+ params[1] = info.taskDescription;
latch.countDown();
}
}
@@ -211,75 +201,71 @@
@Test
@Presubmit
public void testTaskChangeCallBacks() throws Exception {
- final Object[] params = new Object[2];
final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1);
final CountDownLatch taskMovedToFrontLatch = new CountDownLatch(1);
final CountDownLatch taskRemovedLatch = new CountDownLatch(1);
final CountDownLatch taskRemovalStartedLatch = new CountDownLatch(1);
- final CountDownLatch onDetachedFromWindowLatch = new CountDownLatch(1);
+ final int[] expectedTaskId = { -1 };
+ final int[] receivedTaskId = { -1 };
+ final ComponentName expectedName = new ComponentName(getInstrumentation().getContext(),
+ ActivityTaskChangeCallbacks.class);
registerTaskStackChangedListener(new TaskStackListener() {
@Override
- public void onTaskCreated(int taskId, ComponentName componentName)
- throws RemoteException {
- params[0] = taskId;
- params[1] = componentName;
- taskCreatedLaunchLatch.countDown();
+ public void onTaskCreated(int taskId, ComponentName componentName) {
+ receivedTaskId[0] = taskId;
+ if (expectedName.equals(componentName)) {
+ taskCreatedLaunchLatch.countDown();
+ }
}
@Override
- public void onTaskMovedToFront(int taskId) throws RemoteException {
- params[0] = taskId;
+ public void onTaskMovedToFront(RunningTaskInfo info) {
+ receivedTaskId[0] = info.taskId;
taskMovedToFrontLatch.countDown();
}
@Override
- public void onTaskRemovalStarted(int taskId) {
- params[0] = taskId;
- taskRemovalStartedLatch.countDown();
+ public void onTaskRemovalStarted(RunningTaskInfo info) {
+ if (expectedTaskId[0] == info.taskId) {
+ taskRemovalStartedLatch.countDown();
+ }
}
@Override
- public void onTaskRemoved(int taskId) throws RemoteException {
- if (taskCreatedLaunchLatch.getCount() == 1) {
- // The test activity hasn't started. Ignore the noise from previous test.
- return;
+ public void onTaskRemoved(int taskId) {
+ if (expectedTaskId[0] == taskId) {
+ taskRemovedLatch.countDown();
}
- params[0] = taskId;
- taskRemovedLatch.countDown();
}
});
final ActivityTaskChangeCallbacks activity =
(ActivityTaskChangeCallbacks) startTestActivity(ActivityTaskChangeCallbacks.class);
- activity.setDetachedFromWindowLatch(onDetachedFromWindowLatch);
- final int id = activity.getTaskId();
+ expectedTaskId[0] = activity.getTaskId();
// Test for onTaskCreated and onTaskMovedToFront
waitForCallback(taskMovedToFrontLatch);
assertEquals(0, taskCreatedLaunchLatch.getCount());
- assertEquals(id, params[0]);
- ComponentName componentName = (ComponentName) params[1];
- assertEquals(ActivityTaskChangeCallbacks.class.getName(), componentName.getClassName());
+ assertEquals(expectedTaskId[0], receivedTaskId[0]);
+ // Ensure that the window is attached before removal so there will be a detached callback.
+ waitForCallback(activity.mOnAttachedToWindowCountDownLatch);
// Test for onTaskRemovalStarted.
assertEquals(1, taskRemovalStartedLatch.getCount());
assertEquals(1, taskRemovedLatch.getCount());
activity.finishAndRemoveTask();
waitForCallback(taskRemovalStartedLatch);
// onTaskRemovalStarted happens before the activity's window is removed.
- assertFalse(activity.mOnDetachedFromWindowCalled);
- assertEquals(id, params[0]);
+ assertEquals(1, activity.mOnDetachedFromWindowCountDownLatch.getCount());
// Test for onTaskRemoved.
waitForCallback(taskRemovedLatch);
- assertEquals(id, params[0]);
- waitForCallback(onDetachedFromWindowLatch);
- assertTrue(activity.mOnDetachedFromWindowCalled);
+ waitForCallback(activity.mOnDetachedFromWindowCountDownLatch);
}
@Test
public void testTaskDisplayChanged() throws Exception {
- int virtualDisplayId = mVirtualDisplay.getDisplay().getDisplayId();
+ final int virtualDisplayId = createVirtualDisplay().getDisplay().getDisplayId();
// Launch a Activity inside VirtualDisplay
CountDownLatch displayChangedLatch1 = new CountDownLatch(1);
@@ -498,18 +484,18 @@
}
public static class ActivityTaskChangeCallbacks extends TestActivity {
- public boolean mOnDetachedFromWindowCalled = false;
- private CountDownLatch mOnDetachedFromWindowCountDownLatch;
+ final CountDownLatch mOnAttachedToWindowCountDownLatch = new CountDownLatch(1);
+ final CountDownLatch mOnDetachedFromWindowCountDownLatch = new CountDownLatch(1);
+
+ @Override
+ public void onAttachedToWindow() {
+ mOnAttachedToWindowCountDownLatch.countDown();
+ }
@Override
public void onDetachedFromWindow() {
- mOnDetachedFromWindowCalled = true;
mOnDetachedFromWindowCountDownLatch.countDown();
}
-
- void setDetachedFromWindowLatch(CountDownLatch countDownLatch) {
- mOnDetachedFromWindowCountDownLatch = countDownLatch;
- }
}
public static class ActivityInVirtualDisplay extends TestActivity {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 0a8b2e7..fe41734 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -44,6 +44,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.google.common.truth.Truth.assertThat;
@@ -66,6 +67,7 @@
import static org.mockito.Mockito.never;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.content.ComponentName;
@@ -1424,6 +1426,29 @@
verify(task2).moveToFrontInner(anyString(), isNull());
}
+ @Test
+ public void testResumeTask_doNotResumeTaskFragmentBehindTranslucent() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment tfBehind = createTaskFragmentWithParentTask(
+ task, false /* createEmbeddedTask */);
+ final TaskFragment tfFront = createTaskFragmentWithParentTask(
+ task, false /* createEmbeddedTask */);
+ spyOn(tfFront);
+ doReturn(true).when(tfFront).isTranslucent(any());
+
+ // TaskFragment behind another translucent TaskFragment should not be resumed.
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ tfBehind.getVisibility(null /* starting */));
+ assertTrue(tfBehind.isFocusable());
+ assertFalse(tfBehind.canBeResumed(null /* starting */));
+
+ spyOn(tfBehind);
+ task.resumeTopActivityUncheckedLocked(null /* prev */, ActivityOptions.makeBasic(),
+ false /* deferPause */);
+
+ verify(tfBehind, never()).resumeTopActivity(any(), any(), anyBoolean());
+ }
+
private Task getTestTask() {
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
return task.getBottomMostTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index ca2b4ae..ec8ec2b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -252,6 +252,11 @@
assertFalse(appWindow.canBeImeTarget());
appWindow.mActivityRecord.setWindowingMode(initialMode);
+ // Verify that app window can still be IME target as long as it is visible (even if
+ // it is going to become invisible).
+ appWindow.mActivityRecord.mVisibleRequested = false;
+ assertTrue(appWindow.canBeImeTarget());
+
// Make windows invisible
appWindow.hide(false /* doAnimation */, false /* requestAnim */);
imeWindow.hide(false /* doAnimation */, false /* requestAnim */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index a985de5..34038c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -132,6 +132,9 @@
// Default base activity name
private static final String DEFAULT_COMPONENT_CLASS_NAME = ".BarActivity";
+ // An id appended to the end of the component name to make it unique
+ static int sCurrentActivityId = 0;
+
ActivityTaskManagerService mAtm;
RootWindowContainer mRootWindowContainer;
ActivityTaskSupervisor mSupervisor;
@@ -895,13 +898,16 @@
doReturn(100).when(hardwareBuffer).getHeight();
}
+ private static ComponentName getUniqueComponentName() {
+ return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
+ DEFAULT_COMPONENT_CLASS_NAME + sCurrentActivityId++);
+ }
+
/**
* Builder for creating new activities.
*/
protected static class ActivityBuilder {
static final int DEFAULT_FAKE_UID = 12345;
- // An id appended to the end of the component name to make it unique
- private static int sCurrentActivityId = 0;
private final ActivityTaskManagerService mService;
@@ -1077,9 +1083,7 @@
ActivityRecord buildInner() {
if (mComponent == null) {
- final int id = sCurrentActivityId++;
- mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
- DEFAULT_COMPONENT_CLASS_NAME + id);
+ mComponent = getUniqueComponentName();
}
Intent intent = new Intent();
@@ -1388,8 +1392,7 @@
if (mIntent == null) {
mIntent = new Intent();
if (mComponent == null) {
- mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
- DEFAULT_COMPONENT_CLASS_NAME);
+ mComponent = getUniqueComponentName();
}
mIntent.setComponent(mComponent);
mIntent.setFlags(mFlags);
@@ -1422,10 +1425,11 @@
doNothing().when(rootTask).startActivityLocked(
any(), any(), anyBoolean(), anyBoolean(), any(), any());
- // Create child task with activity.
+ // Create child activity.
if (mCreateActivity) {
new ActivityBuilder(mSupervisor.mService)
.setTask(task)
+ .setComponent(mComponent)
.build();
if (mOnTop) {
// We move the task to front again in order to regain focus after activity
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 7861b11..37b4e65 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -632,8 +632,9 @@
* post-dial digits are passed.
* <p>
* Calls with a {@link Call.Details#getHandlePresentation()} of
- * {@link TelecomManager#PRESENTATION_RESTRICTED}, {@link TelecomManager#PRESENTATION_UNKNOWN}
- * or {@link TelecomManager#PRESENTATION_PAYPHONE} presentation are not provided to the
+ * {@link TelecomManager#PRESENTATION_RESTRICTED}, {@link TelecomManager#PRESENTATION_UNKNOWN},
+ * {@link TelecomManager#PRESENTATION_UNAVAILABLE} or
+ * {@link TelecomManager#PRESENTATION_PAYPHONE} presentation are not provided to the
* {@link CallScreeningService}.
*
* @param callDetails Information about a new call, see {@link Call.Details}.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 0dc899e..6279bf8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -990,6 +990,11 @@
*/
public static final int PRESENTATION_PAYPHONE = 4;
+ /**
+ * Indicates that the address or number of a call is unavailable.
+ */
+ public static final int PRESENTATION_UNAVAILABLE = 5;
+
/*
* Values for the adb property "persist.radio.videocall.audio.output"
@@ -1006,7 +1011,7 @@
@IntDef(
prefix = { "PRESENTATION_" },
value = {PRESENTATION_ALLOWED, PRESENTATION_RESTRICTED, PRESENTATION_UNKNOWN,
- PRESENTATION_PAYPHONE})
+ PRESENTATION_PAYPHONE, PRESENTATION_UNAVAILABLE})
public @interface Presentation {}
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index fabe612..184e154 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
@@ -261,6 +262,14 @@
public static final String EXTRA_RESOLUTION_PORT_INDEX =
"android.service.euicc.extra.RESOLUTION_PORT_INDEX";
+ /**
+ * Intent extra set for resolution requests containing a bool indicating whether to use the
+ * given port index. For example, if {@link #switchToSubscription(int, PendingIntent)} is
+ * called, then no portIndex has been provided by the caller, and this extra will be false.
+ */
+ public static final String EXTRA_RESOLUTION_USE_PORT_INDEX =
+ "android.service.euicc.extra.RESOLUTION_USE_PORT_INDEX";
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "RESULT_" }, value = {
@@ -852,14 +861,19 @@
}
@Override
public void switchToSubscription(int slotId, int portIndex, String iccid,
- boolean forceDeactivateSim, ISwitchToSubscriptionCallback callback) {
+ boolean forceDeactivateSim, ISwitchToSubscriptionCallback callback,
+ boolean usePortIndex) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
- // TODO(b/207392528: use portIndex API once implemented)
- int result =
- EuiccService.this.onSwitchToSubscription(
- slotId, iccid, forceDeactivateSim);
+ int result = 0;
+ if (usePortIndex) {
+ result = EuiccService.this.onSwitchToSubscriptionWithPort(
+ slotId, portIndex, iccid, forceDeactivateSim);
+ } else {
+ result = EuiccService.this.onSwitchToSubscription(
+ slotId, iccid, forceDeactivateSim);
+ }
try {
callback.onComplete(result);
} catch (RemoteException e) {
diff --git a/telephony/java/android/service/euicc/IEuiccService.aidl b/telephony/java/android/service/euicc/IEuiccService.aidl
index aa30c9e..030e11a 100644
--- a/telephony/java/android/service/euicc/IEuiccService.aidl
+++ b/telephony/java/android/service/euicc/IEuiccService.aidl
@@ -49,7 +49,7 @@
void getEuiccInfo(int slotId, in IGetEuiccInfoCallback callback);
void deleteSubscription(int slotId, String iccid, in IDeleteSubscriptionCallback callback);
void switchToSubscription(int slotId, int portIndex, String iccid, boolean forceDeactivateSim,
- in ISwitchToSubscriptionCallback callback);
+ in ISwitchToSubscriptionCallback callback, boolean useLegacyApi);
void updateSubscriptionNickname(int slotId, String iccid, String nickname,
in IUpdateSubscriptionNicknameCallback callback);
void eraseSubscriptions(int slotId, in IEraseSubscriptionsCallback callback);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 82113f2..8e55f75 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -798,6 +798,14 @@
"carrier_cross_sim_ims_available_bool";
/**
+ * Flag specifying whether cross sim calling on opportunistic data is supported for carrier.
+ * When {@code false} the carrier does not support cross sim calling on opportunistic data.
+ * When {@code true} the carrier does support cross sim calling on opportunistic data.
+ */
+ public static final String KEY_ENABLE_CROSS_SIM_CALLING_ON_OPPORTUNISTIC_DATA_BOOL =
+ "enable_cross_sim_calling_on_opportunistic_data_bool";
+
+ /**
* Specifies a map from dialstrings to replacements for roaming network service numbers which
* cannot be replaced on the carrier side.
* <p>
@@ -2265,6 +2273,7 @@
* android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE
* android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE
* android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN
+ * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE
*
* <p>
* 1. For Single SIM(SS) device, it can be customized in both carrier_config_mccmnc.xml
@@ -5587,6 +5596,7 @@
sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_ENABLE_CROSS_SIM_CALLING_ON_OPPORTUNISTIC_DATA_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 389cc6d..2e5402c5 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -16,7 +16,6 @@
package android.telephony.euicc;
import android.Manifest;
-import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,7 +29,6 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
-import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.telephony.TelephonyFrameworkInitializer;
@@ -38,13 +36,11 @@
import android.telephony.euicc.EuiccCardManager.ResetOption;
import com.android.internal.telephony.euicc.IEuiccController;
-import com.android.internal.telephony.euicc.IResultCallback;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.Executor;
import java.util.stream.Collectors;
/**
@@ -221,20 +217,6 @@
"android.telephony.euicc.action.START_EUICC_ACTIVATION";
/**
- * Result codes passed to the ResultListener by
- * {@link #switchToSubscription(int, int, Executor, ResultListener)}
- *
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"EMBEDDED_SUBSCRIPTION_RESULT_"}, value = {
- EMBEDDED_SUBSCRIPTION_RESULT_OK,
- EMBEDDED_SUBSCRIPTION_RESULT_ERROR,
- EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR
- })
- public @interface ResultCode{}
-
- /**
* Result code for an operation indicating that the operation succeeded.
*/
public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0;
@@ -1147,7 +1129,7 @@
* @param callbackIntent a PendingIntent to launch when the operation completes.
*
* @deprecated From T, callers should use
- * {@link #switchToSubscription(int, int, Executor, ResultListener)} instead to specify a port
+ * {@link #switchToSubscription(int, int, PendingIntent)} instead to specify a port
* index on the card to switch to.
*/
@Deprecated
@@ -1190,47 +1172,24 @@
* permission, or the calling app must be authorized to manage the active subscription on
* the target eUICC.
* @param portIndex the index of the port to target for the enabled subscription
- * @param executor an Executor on which to run the callback
- * @param callback a {@link ResultListener} which will run when the operation completes
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
*/
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void switchToSubscription(int subscriptionId, int portIndex,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull ResultListener callback) {
+ @NonNull PendingIntent callbackIntent) {
if (!isEnabled()) {
- sendUnavailableErrorToCallback(executor, callback);
+ sendUnavailableError(callbackIntent);
return;
}
try {
- IResultCallback internalCallback = new IResultCallback.Stub() {
- @Override
- public void onComplete(int result, Intent resultIntent) {
- executor.execute(() -> Binder.withCleanCallingIdentity(
- () -> callback.onComplete(result, resultIntent)));
- }
- };
- getIEuiccController().switchToSubscriptionWithPort(mCardId, portIndex,
- subscriptionId, mContext.getOpPackageName(), internalCallback);
+ getIEuiccController().switchToSubscriptionWithPort(mCardId,
+ subscriptionId, portIndex, mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Callback to receive the result of an EuiccManager API.
- */
- public interface ResultListener {
- /**
- * Called on completion of some operation.
- * @param resultCode representing success or specific failure of the operation
- * (See {@link ResultCode})
- * @param resultIntent an intent used to start a resolution activity when an error
- * occurs that can be resolved by the user
- */
- void onComplete(@ResultCode int resultCode, @Nullable Intent resultIntent);
- }
-
- /**
* Update the nickname for the given subscription.
*
* <p>Requires that the calling app has carrier privileges according to the metadata of the
@@ -1501,13 +1460,6 @@
}
}
- private static void sendUnavailableErrorToCallback(@NonNull Executor executor,
- ResultListener callback) {
- Integer result = EMBEDDED_SUBSCRIPTION_RESULT_ERROR;
- executor.execute(() ->
- Binder.withCleanCallingIdentity(() -> callback.onComplete(result, null)));
- }
-
private static IEuiccController getIEuiccController() {
return IEuiccController.Stub.asInterface(
TelephonyFrameworkInitializer
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 93e1058..7e2d80e 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -317,6 +317,10 @@
* Payphone presentation for Originating Identity.
*/
public static final int OIR_PRESENTATION_PAYPHONE = 4;
+ /**
+ * Unavailable presentation for Originating Identity.
+ */
+ public static final int OIR_PRESENTATION_UNAVAILABLE = 5;
//Values for EXTRA_DIALSTRING
/**
@@ -989,6 +993,8 @@
return ImsCallProfile.OIR_PRESENTATION_PAYPHONE;
case PhoneConstants.PRESENTATION_UNKNOWN:
return ImsCallProfile.OIR_PRESENTATION_UNKNOWN;
+ case PhoneConstants.PRESENTATION_UNAVAILABLE:
+ return ImsCallProfile.OIR_PRESENTATION_UNAVAILABLE;
default:
return ImsCallProfile.OIR_DEFAULT;
}
@@ -1017,6 +1023,8 @@
return PhoneConstants.PRESENTATION_ALLOWED;
case ImsCallProfile.OIR_PRESENTATION_PAYPHONE:
return PhoneConstants.PRESENTATION_PAYPHONE;
+ case ImsCallProfile.OIR_PRESENTATION_UNAVAILABLE:
+ return PhoneConstants.PRESENTATION_UNAVAILABLE;
case ImsCallProfile.OIR_PRESENTATION_UNKNOWN:
return PhoneConstants.PRESENTATION_UNKNOWN;
default:
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index f650246..813e80e 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -95,6 +95,7 @@
public static final int PRESENTATION_UNKNOWN = 3; // no specified or unknown by network
@UnsupportedAppUsage
public static final int PRESENTATION_PAYPHONE = 4; // show pay phone info
+ public static final int PRESENTATION_UNAVAILABLE = 5; // show unavailable
public static final String PHONE_NAME_KEY = "phoneName";
public static final String DATA_NETWORK_TYPE_KEY = "networkType";
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 7f5982f..dda95b1 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -22,8 +22,6 @@
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
-import com.android.internal.telephony.euicc.IResultCallback;
-
import java.util.List;
/** @hide */
@@ -45,8 +43,8 @@
in PendingIntent callbackIntent);
oneway void switchToSubscription(int cardId, int subscriptionId, String callingPackage,
in PendingIntent callbackIntent);
- oneway void switchToSubscriptionWithPort(int cardId, int portIndex, int subscriptionId,
- String callingPackage, in IResultCallback callback);
+ oneway void switchToSubscriptionWithPort(int cardId, int subscriptionId, int portIndex,
+ String callingPackage, in PendingIntent callbackIntent);
oneway void updateSubscriptionNickname(int cardId, int subscriptionId, String nickname,
String callingPackage, in PendingIntent callbackIntent);
oneway void eraseSubscriptions(int cardId, in PendingIntent callbackIntent);
diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk
index ce5e4cf..284008c 100644
--- a/test-legacy/Android.mk
+++ b/test-legacy/Android.mk
@@ -41,6 +41,6 @@
include $(BUILD_STATIC_JAVA_LIBRARY)
# Archive a copy of the classes.jar in SDK build.
-$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.legacy.jar)
+$(call dist-for-goals,sdk,$(full_classes_jar):android.test.legacy.jar)
endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index 59e8dc8..8fe0029 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -37,16 +37,21 @@
.launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) {
- val button = device.wait(
- Until.findObject(By.res(getPackage(), "launch_second_activity")),
- FIND_TIMEOUT)
+ val launchActivityButton = By.res(getPackage(), LAUNCH_SECOND_ACTIVITY)
+ val button = device.wait(Until.findObject(launchActivityButton), FIND_TIMEOUT)
require(button != null) {
"Button not found, this usually happens when the device " +
"was left in an unknown state (e.g. in split screen)"
}
button.click()
+
+ device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT)
wmHelper.waitForAppTransitionIdle()
wmHelper.waitForFullScreenApp(component)
}
+
+ companion object {
+ private const val LAUNCH_SECOND_ACTIVITY = "launch_second_activity"
+ }
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index a7a9fe2..19e2c92 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -86,8 +86,8 @@
// [Step1]: Swipe right from imeTestApp to testApp task
createTag(TAG_IME_VISIBLE)
val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
- device.swipe(0, displayBounds.bounds.height(),
- displayBounds.bounds.width(), displayBounds.bounds.height(), 50)
+ device.swipe(0, displayBounds.bounds.height,
+ displayBounds.bounds.width, displayBounds.bounds.height, 50)
wmHelper.waitForFullScreenApp(testApp.component)
wmHelper.waitForAppTransitionIdle()
@@ -96,8 +96,8 @@
transitions {
// [Step2]: Swipe left to back to imeTestApp task
val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
- device.swipe(displayBounds.bounds.width(), displayBounds.bounds.height(),
- 0, displayBounds.bounds.height(), 50)
+ device.swipe(displayBounds.bounds.width, displayBounds.bounds.height,
+ 0, displayBounds.bounds.height, 50)
wmHelper.waitForFullScreenApp(imeTestApp.component)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 769cb1a..882e128 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -148,22 +148,28 @@
val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
testSpec.assertLayers {
- this.coversExactly(displayBounds, LAUNCH_NEW_TASK_ACTIVITY)
+ this.invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
+ it.visibleRegion(LAUNCH_NEW_TASK_ACTIVITY).coversExactly(displayBounds)
+ }
.isInvisible(bgColorLayer)
.then()
// Transitioning
.isVisible(bgColorLayer)
.then()
// Fully transitioned to simple SIMPLE_ACTIVITY
- .coversExactly(displayBounds, SIMPLE_ACTIVITY)
+ .invoke("SIMPLE_ACTIVITY coversExactly displayBounds") {
+ it.visibleRegion(SIMPLE_ACTIVITY).coversExactly(displayBounds)
+ }
.isInvisible(bgColorLayer)
.then()
// Transitioning back
.isVisible(bgColorLayer)
.then()
// Fully transitioned back to LAUNCH_NEW_TASK_ACTIVITY
+ .invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
+ it.visibleRegion(LAUNCH_NEW_TASK_ACTIVITY).coversExactly(displayBounds)
+ }
.isInvisible(bgColorLayer)
- .coversExactly(displayBounds, LAUNCH_NEW_TASK_ACTIVITY)
}
}
diff --git a/tests/HwAccelerationTest/Android.bp b/tests/HwAccelerationTest/Android.bp
index e618ed1..51848f2 100644
--- a/tests/HwAccelerationTest/Android.bp
+++ b/tests/HwAccelerationTest/Android.bp
@@ -32,6 +32,10 @@
"**/*.java",
"**/*.kt",
],
+ static_libs: [
+ "androidx.cardview_cardview",
+ ],
+
platform_apis: true,
certificate: "platform",
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 22fe424..b0ccbd1 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -789,6 +789,15 @@
</intent-filter>
</activity>
+ <activity android:name="RenderEffectViewActivity"
+ android:label="RenderEffect/View"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="StretchShaderActivity"
android:label="RenderEffect/Stretch"
android:exported="true">
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scratches.png b/tests/HwAccelerationTest/res/drawable-nodpi/scratches.png
new file mode 100644
index 0000000..cc8adf1
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-nodpi/scratches.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg b/tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg
new file mode 100644
index 0000000..b5aff10
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/layout/view_runtime_shader.xml b/tests/HwAccelerationTest/res/layout/view_runtime_shader.xml
new file mode 100644
index 0000000..b91377d
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/view_runtime_shader.xml
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:id="@+id/TopLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="Sample Card #1"/>
+
+ <androidx.cardview.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:layout_marginBottom="16dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:minHeight="148dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:paddingBottom="8dp">
+
+ <ImageView
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginRight="8dp"
+ android:contentDescription="Logo"
+ android:src="@drawable/icon"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:layout_marginStart="8dp"
+ android:layout_marginLeft="8dp"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:text="Image Transition"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="Touch the image to trigger the animation"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <com.android.test.hwui.BitmapTransitionView
+ android:layout_width="match_parent"
+ android:layout_height="194dp"
+ android:padding="8dp"/>
+
+ </LinearLayout>
+
+ </androidx.cardview.widget.CardView>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="Sample Card #2"/>
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/CardView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:layout_marginBottom="16dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:minHeight="148dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:paddingBottom="8dp">
+
+ <ImageView
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginRight="8dp"
+ android:contentDescription="Logo"
+ android:src="@drawable/icon"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:layout_marginStart="8dp"
+ android:layout_marginLeft="8dp"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:text="View Group Manipulation"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="Tap the card to trigger the animation"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="194dp"
+ android:background="@android:color/transparent"
+ android:src="@drawable/weather_2"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:paddingBottom="8dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <RatingBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="Card Rating"
+ android:isIndicator="true"
+ android:numStars="5"
+ android:rating="4.5"
+
+ android:stepSize="0.5"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="Category 4.5 Storm"/>
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:maxLines="3"
+ android:textIsSelectable="true"
+ android:text="Lorem ipsum dolor sit amet, nec no nominavi scaevola. Per et
+ sint sapientem, nobis perpetua salutandi mei te. Quo tamquam probatus
+ reprehendunt in. Eos esse purto eruditi ea. Enim tation persius ut sea,
+ eos ad consul populo. Ne eum solet altera. Cibo eligendi et est, electram
+ theophrastus te vel eu."/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </androidx.cardview.widget.CardView>
+ </LinearLayout>
+</ScrollView>
diff --git a/tests/HwAccelerationTest/res/values/styles.xml b/tests/HwAccelerationTest/res/values/styles.xml
index fa5437f..55f4dd69 100644
--- a/tests/HwAccelerationTest/res/values/styles.xml
+++ b/tests/HwAccelerationTest/res/values/styles.xml
@@ -41,4 +41,5 @@
<item name="android:spotShadowAlpha">1</item>
-->
</style>
+
</resources>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
new file mode 100644
index 0000000..d3ad9e8
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.BitmapShader
+import android.graphics.Canvas
+import android.graphics.ImageDecoder
+import android.graphics.Matrix
+import android.graphics.Paint
+import android.graphics.RuntimeShader
+import android.graphics.Shader
+import android.util.AttributeSet
+import android.view.View
+
+class BitmapTransitionView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : View(context, attrs, defStyleAttr) {
+
+ private val mPaint = Paint()
+ private val mImageA = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.resources, R.drawable.large_photo))
+ private val mImageB = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.resources, R.drawable.very_large_photo))
+ private val mShaderA = BitmapShader(mImageA, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
+ private val mShaderB = BitmapShader(mImageB, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
+ private val mShader = RuntimeShader(AGSL, false)
+ private var mCurrentProgress = -1f
+ private var mForwardProgress = true
+ private var mCurrentAnimator = ValueAnimator.ofFloat(-1f, 1f)
+
+ init {
+ isClickable = true
+
+ mCurrentAnimator.duration = 1500
+ mCurrentAnimator.addUpdateListener { animation ->
+ mCurrentProgress = animation.animatedValue as Float
+ postInvalidate()
+ }
+ }
+
+ override fun performClick(): Boolean {
+ if (super.performClick()) return true
+
+ if (mCurrentAnimator.isRunning) {
+ mCurrentAnimator.reverse()
+ return true
+ }
+
+ if (mForwardProgress) {
+ mCurrentAnimator.setFloatValues(-1f, 1f)
+ mForwardProgress = false
+ } else {
+ mCurrentAnimator.setFloatValues(1f, -1f)
+ mForwardProgress = true
+ }
+
+ mCurrentAnimator.start()
+ postInvalidate()
+ return true
+ }
+
+ override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
+ val matrixA = Matrix()
+ val matrixB = Matrix()
+
+ matrixA.postScale(width.toFloat() / mImageA.width, height.toFloat() / mImageA.height)
+ matrixB.postScale(width.toFloat() / mImageB.width, height.toFloat() / mImageB.height)
+
+ mShaderA.setLocalMatrix(matrixA)
+ mShaderB.setLocalMatrix(matrixB)
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ mShader.setInputShader("imageA", mShaderA)
+ mShader.setInputShader("imageB", mShaderB)
+ mShader.setIntUniform("imageDimensions", width, height)
+ mShader.setFloatUniform("progress", mCurrentProgress)
+
+ mPaint.shader = mShader
+ canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), mPaint)
+ }
+
+ private companion object {
+ const val AGSL = """
+ uniform shader imageA;
+ uniform shader imageB;
+ uniform ivec2 imageDimensions;
+ uniform float progress;
+
+ const vec2 iSize = vec2(48.0, 48.0);
+ const float iDir = 0.5;
+ const float iRand = 0.81;
+
+ float hash12(vec2 p) {
+ vec3 p3 = fract(vec3(p.xyx) * .1031);
+ p3 += dot(p3, p3.yzx + 33.33);
+ return fract((p3.x + p3.y) * p3.z);
+ }
+
+ float ramp(float2 p) {
+ return mix(hash12(p),
+ dot(p/vec2(imageDimensions), float2(iDir, 1 - iDir)),
+ iRand);
+ }
+
+ half4 main(float2 p) {
+ float2 lowRes = p / iSize;
+ float2 cellCenter = (floor(lowRes) + 0.5) * iSize;
+ float2 posInCell = fract(lowRes) * 2 - 1;
+
+ float v = ramp(cellCenter) + progress;
+ float distToCenter = max(abs(posInCell.x), abs(posInCell.y));
+
+ return distToCenter > v ? imageA.eval(p).rgb1 : imageB.eval(p).rgb1;
+ }
+ """
+ }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
new file mode 100644
index 0000000..06280d2
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui
+
+import android.animation.ValueAnimator
+import android.app.Activity
+import android.graphics.Bitmap
+import android.graphics.BitmapShader
+import android.graphics.ImageDecoder
+import android.graphics.Matrix
+import android.graphics.Shader
+import android.graphics.RenderEffect
+import android.graphics.RuntimeShader
+import android.os.Bundle
+import android.view.View
+
+class RenderEffectViewActivity : Activity() {
+
+ private val mDropsShader = RuntimeShader(dropsAGSL, false)
+ private var mDropsAnimator = ValueAnimator.ofFloat(0f, 1f)
+ private var mStartTime = System.currentTimeMillis()
+ private lateinit var mScratchesImage: Bitmap
+ private lateinit var mScratchesShader: BitmapShader
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.view_runtime_shader)
+
+ val dropsView = findViewById<View>(R.id.CardView)!!
+ dropsView.isClickable = true
+ dropsView.setOnClickListener {
+ if (mDropsAnimator.isRunning) {
+ mDropsAnimator.cancel()
+ dropsView.setRenderEffect(null)
+ } else {
+ mDropsAnimator.start()
+ }
+ }
+
+ val imgSource = ImageDecoder.createSource(resources, R.drawable.scratches)
+ mScratchesImage = ImageDecoder.decodeBitmap(imgSource)
+ mScratchesShader = BitmapShader(mScratchesImage,
+ Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
+
+ mDropsAnimator.duration = 1000
+ mDropsAnimator.repeatCount = ValueAnimator.INFINITE
+ mDropsAnimator.addUpdateListener { _ ->
+ val viewWidth = dropsView.width.toFloat()
+ val viewHeight = dropsView.height.toFloat()
+ val scratchesMatrix = Matrix()
+ scratchesMatrix.postScale(viewWidth / mScratchesImage.width,
+ viewHeight / mScratchesImage.height)
+ mScratchesShader.setLocalMatrix(scratchesMatrix)
+
+ mDropsShader.setInputShader("scratches", mScratchesShader)
+ mDropsShader.setFloatUniform("elapsedSeconds",
+ (System.currentTimeMillis() - mStartTime) / 1000f)
+ mDropsShader.setFloatUniform("viewDimensions", viewWidth, viewHeight)
+
+ val dropsEffect = RenderEffect.createRuntimeShaderEffect(mDropsShader, "background")
+ val blurEffect = RenderEffect.createBlurEffect(10f, 10f, Shader.TileMode.CLAMP)
+
+ dropsView.setRenderEffect(RenderEffect.createChainEffect(dropsEffect, blurEffect))
+ }
+ }
+
+ private companion object {
+ const val dropsAGSL = """
+ uniform float elapsedSeconds;
+ uniform vec2 viewDimensions;
+ uniform shader background;
+ uniform shader scratches;
+
+ vec2 dropsUV(vec2 fragCoord ) {
+ vec2 uv = fragCoord.xy / viewDimensions.xy; // 0 <> 1
+ vec2 offs = vec2(0.);
+ return (offs + uv).xy;
+ }
+
+ const vec3 iFrostColorRGB = vec3(0.5, 0.5, 0.5);
+ const float iFrostColorAlpha = .3;
+
+ half4 main(float2 fragCoord) {
+ half4 bg = background.eval(dropsUV(fragCoord)*viewDimensions.xy);
+ float2 scratchCoord = fragCoord.xy / viewDimensions.xy;;
+ scratchCoord += 1.5;
+ scratchCoord = mod(scratchCoord, 1);
+ half scratch = scratches.eval(scratchCoord*viewDimensions.xy).r;
+ bg.rgb = mix(bg.rgb, iFrostColorRGB, iFrostColorAlpha);
+ bg.rgb = mix(bg.rgb, half3(1), pow(scratch,3));
+ return bg;
+ }
+ """
+ }
+}
\ No newline at end of file
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index b21d7b5..426f3be 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -303,11 +303,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
}
@Test
@@ -315,11 +318,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false));
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false));
}
@Test
@@ -327,11 +333,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
}
@Test
@@ -339,11 +348,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false));
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false));
}
@Test
@@ -467,8 +479,7 @@
// Query proper module name
result = getPackageManagerNative().getStagedApexInfo(TEST_APEX_PACKAGE_NAME);
assertThat(result.moduleName).isEqualTo(TEST_APEX_PACKAGE_NAME);
- assertThat(result.hasBootClassPathJars).isTrue();
- assertThat(result.hasSystemServerClassPathJars).isTrue();
+ assertThat(result.hasClassPathJars).isTrue();
InstallUtils.openPackageInstallerSession(sessionId).abandon();
}
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index 68e213b..fe2b018 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -13,15 +13,18 @@
EMOJI_VS = 0xFE0F
LANG_TO_SCRIPT = {
+ 'af': 'Latn',
'as': 'Beng',
'am': 'Latn',
'be': 'Cyrl',
'bg': 'Cyrl',
'bn': 'Beng',
+ 'cs': 'Latn',
'cu': 'Cyrl',
'cy': 'Latn',
'da': 'Latn',
'de': 'Latn',
+ 'el': 'Latn',
'en': 'Latn',
'es': 'Latn',
'et': 'Latn',
@@ -36,19 +39,24 @@
'hy': 'Armn',
'it': 'Latn',
'ja': 'Jpan',
+ 'ka': 'Latn',
'kn': 'Knda',
'ko': 'Kore',
'la': 'Latn',
'lt': 'Latn',
+ 'lv': 'Latn',
'ml': 'Mlym',
'mn': 'Cyrl',
'mr': 'Deva',
'nb': 'Latn',
+ 'nl': 'Latn',
'nn': 'Latn',
'or': 'Orya',
'pa': 'Guru',
'pt': 'Latn',
+ 'sk': 'Latn',
'sl': 'Latn',
+ 'sq': 'Latn',
'ta': 'Taml',
'te': 'Telu',
'tk': 'Latn',