Merge "Add handle suppression to learned state"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6b931b8..5ab694f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -189,7 +189,6 @@
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.net.InetAddress;
-import java.nio.file.Files;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -7293,24 +7292,6 @@
super.remove(path);
}
}
-
- @Override
- public void rename(String oldPath, String newPath) throws ErrnoException {
- try {
- super.rename(oldPath, newPath);
- } catch (ErrnoException e) {
- if (e.errno == OsConstants.EXDEV) {
- Log.v(TAG, "Recovering failed rename " + oldPath + " to " + newPath);
- try {
- Files.move(new File(oldPath).toPath(), new File(newPath).toPath());
- } catch (IOException e2) {
- throw e;
- }
- } else {
- throw e;
- }
- }
- }
}
public static void main(String[] args) {
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index e8b3ca6..1456ff7 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -16,6 +16,7 @@
package android.os;
+import android.media.AudioAttributes;
import android.os.VibrationEffect;
/** {@hide} */
@@ -23,8 +24,8 @@
{
boolean hasVibrator();
boolean hasAmplitudeControl();
- void vibrate(int uid, String opPkg, in VibrationEffect effect, int usageHint, String reason,
- IBinder token);
+ void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes,
+ String reason, IBinder token);
void cancelVibrate(IBinder token);
}
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 4af514a..a5188e7 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -77,16 +77,12 @@
return;
}
try {
- mService.vibrate(uid, opPkg, effect, usageForAttributes(attributes), reason, mToken);
+ mService.vibrate(uid, opPkg, effect, attributes, reason, mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
}
- private static int usageForAttributes(AudioAttributes attributes) {
- return attributes != null ? attributes.getUsage() : AudioAttributes.USAGE_UNKNOWN;
- }
-
@Override
public void cancel() {
if (mService == null) {
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index bf0ef94..2c2c295 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1122,6 +1122,9 @@
if (limit > lineEnd) {
limit = lineEnd;
}
+ if (limit == start) {
+ continue;
+ }
level[limit - lineStart - 1] =
(byte) ((runs[i + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK);
}
@@ -1220,8 +1223,8 @@
}
/**
- * Computes in linear time the results of calling
- * #getHorizontal for all offsets on a line.
+ * Computes in linear time the results of calling #getHorizontal for all offsets on a line.
+ *
* @param line The line giving the offsets we compute information for
* @param clamped Whether to clamp the results to the width of the layout
* @param primary Whether the results should be the primary or the secondary horizontal
@@ -1257,7 +1260,7 @@
TextLine.recycle(tl);
if (clamped) {
- for (int offset = 0; offset <= wid.length; ++offset) {
+ for (int offset = 0; offset < wid.length; ++offset) {
if (wid[offset] > mWidth) {
wid[offset] = mWidth;
}
diff --git a/core/java/android/util/TimingsTraceLog.java b/core/java/android/util/TimingsTraceLog.java
index bc21722..5370645 100644
--- a/core/java/android/util/TimingsTraceLog.java
+++ b/core/java/android/util/TimingsTraceLog.java
@@ -21,10 +21,10 @@
import android.os.SystemClock;
import android.os.Trace;
-import java.util.ArrayDeque;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Deque;
import java.util.List;
/**
@@ -37,16 +37,37 @@
public class TimingsTraceLog {
// Debug boot time for every step if it's non-user build.
private static final boolean DEBUG_BOOT_TIME = !Build.IS_USER;
- private final Deque<Pair<String, Long>> mStartTimes =
- DEBUG_BOOT_TIME ? new ArrayDeque<>() : null;
+
+ // Maximum number of nested calls that are stored
+ private static final int MAX_NESTED_CALLS = 10;
+
+ private final String[] mStartNames;
+ private final long[] mStartTimes;
+
private final String mTag;
- private long mTraceTag;
- private long mThreadId;
+ private final long mTraceTag;
+ private final long mThreadId;
+ private final int mMaxNestedCalls;
+
+ private int mCurrentLevel = -1;
public TimingsTraceLog(String tag, long traceTag) {
+ this(tag, traceTag, DEBUG_BOOT_TIME ? MAX_NESTED_CALLS : -1);
+ }
+
+ @VisibleForTesting
+ public TimingsTraceLog(String tag, long traceTag, int maxNestedCalls) {
mTag = tag;
mTraceTag = traceTag;
mThreadId = Thread.currentThread().getId();
+ mMaxNestedCalls = maxNestedCalls;
+ if (maxNestedCalls > 0) {
+ mStartNames = new String[maxNestedCalls];
+ mStartTimes = new long[maxNestedCalls];
+ } else {
+ mStartNames = null;
+ mStartTimes = null;
+ }
}
/**
@@ -56,27 +77,41 @@
public void traceBegin(String name) {
assertSameThread();
Trace.traceBegin(mTraceTag, name);
- if (DEBUG_BOOT_TIME) {
- mStartTimes.push(Pair.create(name, SystemClock.elapsedRealtime()));
+
+ if (!DEBUG_BOOT_TIME) return;
+
+ if (mCurrentLevel + 1 >= mMaxNestedCalls) {
+ Slog.w(mTag, "not tracing duration of '" + name + "' because already reached "
+ + mMaxNestedCalls + " levels");
+ return;
}
+
+ mCurrentLevel++;
+ mStartNames[mCurrentLevel] = name;
+ mStartTimes[mCurrentLevel] = SystemClock.elapsedRealtime();
}
/**
* End tracing previously {@link #traceBegin(String) started} section.
- * Also {@link #logDuration logs} the duration.
+ *
+ * <p>Also {@link #logDuration logs} the duration.
*/
public void traceEnd() {
assertSameThread();
Trace.traceEnd(mTraceTag);
- if (!DEBUG_BOOT_TIME) {
- return;
- }
- if (mStartTimes.peek() == null) {
+
+ if (!DEBUG_BOOT_TIME) return;
+
+ if (mCurrentLevel < 0) {
Slog.w(mTag, "traceEnd called more times than traceBegin");
return;
}
- Pair<String, Long> event = mStartTimes.pop();
- logDuration(event.first, (SystemClock.elapsedRealtime() - event.second));
+
+ final String name = mStartNames[mCurrentLevel];
+ final long duration = SystemClock.elapsedRealtime() - mStartTimes[mCurrentLevel];
+ mCurrentLevel--;
+
+ logDuration(name, duration);
}
private void assertSameThread() {
@@ -89,7 +124,7 @@
}
/**
- * Log the duration so it can be parsed by external tools for performance reporting
+ * Logs a duration so it can be parsed by external tools for performance reporting.
*/
public void logDuration(String name, long timeMs) {
Slog.d(mTag, name + " took to complete: " + timeMs + "ms");
@@ -105,10 +140,11 @@
*/
@NonNull
public final List<String> getUnfinishedTracesForDebug() {
- if (mStartTimes == null || mStartTimes.isEmpty()) return Collections.emptyList();
-
- final ArrayList<String> stack = new ArrayList<>(mStartTimes.size());
- mStartTimes.descendingIterator().forEachRemaining((pair) -> stack.add(pair.first));
- return stack;
+ if (mStartTimes == null || mCurrentLevel < 0) return Collections.emptyList();
+ final ArrayList<String> list = new ArrayList<>(mCurrentLevel + 1);
+ for (int i = 0; i <= mCurrentLevel; i++) {
+ list.add(mStartNames[i]);
+ }
+ return list;
}
}
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 3e464c6..73dc656 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -131,13 +131,6 @@
return (IGraphicBufferProducer*)env->GetLongField(thiz, fields.producer);
}
-sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) {
- sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
- sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz));
- sp<Surface> surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL);
- return surfaceTextureClient;
-}
-
bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) {
jclass surfaceTextureClass = env->FindClass(kSurfaceTextureClassPathName);
return env->IsInstanceOf(thiz, surfaceTextureClass);
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 719cf74..87adbe8 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -26,6 +26,7 @@
#include "core_jni_helpers.h"
#include "android_runtime/android_view_Surface.h"
#include "android_runtime/android_graphics_SurfaceTexture.h"
+#include "surfacetexture/SurfaceTexture.h"
#include <gui/Surface.h>
#include <gui/IGraphicBufferProducer.h>
@@ -394,10 +395,17 @@
return anw;
}
+static sp<ANativeWindow> getSurfaceTextureNativeWindow(JNIEnv* env, jobject thiz) {
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz));
+ sp<Surface> surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL);
+ return surfaceTextureClient;
+}
+
static sp<ANativeWindow> getNativeWindowFromTexture(JNIEnv* env, jobject surfaceTexture) {
sp<ANativeWindow> anw;
if (surfaceTexture) {
- anw = android_SurfaceTexture_getNativeWindow(env, surfaceTexture);
+ anw = getSurfaceTextureNativeWindow(env, surfaceTexture);
if (env->ExceptionCheck()) {
return NULL;
}
diff --git a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h
index 0ad2587..d3ff959 100644
--- a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h
+++ b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h
@@ -17,8 +17,6 @@
#ifndef _ANDROID_GRAPHICS_SURFACETEXTURE_H
#define _ANDROID_GRAPHICS_SURFACETEXTURE_H
-#include <android/native_window.h>
-
#include "jni.h"
namespace android {
@@ -26,7 +24,6 @@
class IGraphicBufferProducer;
class SurfaceTexture;
-extern sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz);
extern bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz);
/* Gets the underlying C++ SurfaceTexture object from a SurfaceTexture Java object. */
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 990161a..93a6b15 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -743,6 +743,9 @@
assertPrimaryIsTrailingPrevious(
RTL + LRI + RTL + LTR + PDI + RTL,
new boolean[]{false, false, true, false, false, false, false});
+ assertPrimaryIsTrailingPrevious(
+ "",
+ new boolean[]{false});
}
}
diff --git a/core/tests/coretests/src/android/util/TimingsTraceLogTest.java b/core/tests/coretests/src/android/util/TimingsTraceLogTest.java
deleted file mode 100644
index f76471c..0000000
--- a/core/tests/coretests/src/android/util/TimingsTraceLogTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 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.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Trace;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Tests for {@link TimingsTraceLog}.
- *
- * <p>Usage: {@code atest FrameworksCoreTests:android.util.TimingsTraceLogTest}
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TimingsTraceLogTest {
-
- @Test
- public void testDifferentThreads() throws Exception {
- TimingsTraceLog log = new TimingsTraceLog("TEST", Trace.TRACE_TAG_APP);
- // Should be able to log on the same thread
- log.traceBegin("test");
- log.traceEnd();
- final List<String> errors = new ArrayList<>();
- // Calling from a different thread should fail
- Thread t = new Thread(() -> {
- try {
- log.traceBegin("test");
- errors.add("traceBegin should fail on a different thread");
- } catch (IllegalStateException expected) {
- }
- try {
- log.traceEnd();
- errors.add("traceEnd should fail on a different thread");
- } catch (IllegalStateException expected) {
- }
- // Verify that creating a new log will work
- TimingsTraceLog log2 = new TimingsTraceLog("TEST", Trace.TRACE_TAG_APP);
- log2.traceBegin("test");
- log2.traceEnd();
-
- });
- t.start();
- t.join();
- assertThat(errors).isEmpty();
- }
-
- @Test
- public void testGetUnfinishedTracesForDebug() {
- TimingsTraceLog log = new TimingsTraceLog("TEST", Trace.TRACE_TAG_APP);
- assertThat(log.getUnfinishedTracesForDebug()).isEmpty();
-
- log.traceBegin("One");
- assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder();
-
- log.traceBegin("Two");
- assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One", "Two").inOrder();
-
- log.traceEnd();
- assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder();
-
- log.traceEnd();
- assertThat(log.getUnfinishedTracesForDebug()).isEmpty();
- }
-}
diff --git a/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
new file mode 100644
index 0000000..5dc44d2
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 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.util;
+
+import static android.os.Trace.TRACE_TAG_APP;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.contains;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.matches;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+
+import android.os.Trace;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.MockedVoidMethod;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link TimingsTraceLog}.
+ *
+ * <p>Usage: {@code atest FrameworksMockingCoreTests:android.util.TimingsTraceLogTest}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TimingsTraceLogTest {
+
+ private static final String TAG = "TEST";
+
+ private MockitoSession mSession;
+
+ @Before
+ public final void startMockSession() {
+ mSession = mockitoSession()
+ .spyStatic(Slog.class)
+ .spyStatic(Trace.class)
+ .startMocking();
+ }
+
+ @After
+ public final void finishMockSession() {
+ mSession.finishMocking();
+ }
+
+ @Test
+ public void testDifferentThreads() throws Exception {
+ TimingsTraceLog log = new TimingsTraceLog(TAG, TRACE_TAG_APP);
+ // Should be able to log on the same thread
+ log.traceBegin("test");
+ log.traceEnd();
+ final List<String> errors = new ArrayList<>();
+ // Calling from a different thread should fail
+ Thread t = new Thread(() -> {
+ try {
+ log.traceBegin("test");
+ errors.add("traceBegin should fail on a different thread");
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ log.traceEnd();
+ errors.add("traceEnd should fail on a different thread");
+ } catch (IllegalStateException expected) {
+ }
+ // Verify that creating a new log will work
+ TimingsTraceLog log2 = new TimingsTraceLog(TAG, TRACE_TAG_APP);
+ log2.traceBegin("test");
+ log2.traceEnd();
+
+ });
+ t.start();
+ t.join();
+ assertThat(errors).isEmpty();
+ }
+
+ @Test
+ public void testGetUnfinishedTracesForDebug() {
+ TimingsTraceLog log = new TimingsTraceLog("TEST", Trace.TRACE_TAG_APP);
+ assertThat(log.getUnfinishedTracesForDebug()).isEmpty();
+
+ log.traceBegin("One");
+ assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder();
+
+ log.traceBegin("Two");
+ assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One", "Two").inOrder();
+
+ log.traceEnd();
+ assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder();
+
+ log.traceEnd();
+ assertThat(log.getUnfinishedTracesForDebug()).isEmpty();
+ }
+
+ @Test
+ public void testLogDuration() throws Exception {
+ TimingsTraceLog log = new TimingsTraceLog(TAG, TRACE_TAG_APP, 10);
+ log.logDuration("logro", 42);
+ verify((MockedVoidMethod) () -> Slog.d(eq(TAG), contains("logro took to complete: 42ms")));
+ }
+
+ @Test
+ public void testOneLevel() throws Exception {
+ TimingsTraceLog log = new TimingsTraceLog(TAG, TRACE_TAG_APP, 10);
+ log.traceBegin("test");
+ log.traceEnd();
+
+ verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "test"));
+ verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP));
+ verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("test took to complete: \\dms")));
+ }
+
+ @Test
+ public void testMultipleLevels() throws Exception {
+ TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_APP, 10);
+ log.traceBegin("L1");
+ log.traceBegin("L2");
+ log.traceEnd();
+ log.traceEnd();
+
+ verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L1"));
+ verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L2"));
+ verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(2)); // L1 and L2
+
+ verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L2 took to complete: \\d+ms")));
+ verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L1 took to complete: \\d+ms")));
+ }
+
+ @Test
+ public void testTooManyLevels() throws Exception {
+ TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_APP, 2);
+
+ log.traceBegin("L1"); // ok
+ log.traceBegin("L2"); // ok
+ log.traceBegin("L3"); // logging ignored ( > 2)
+
+ log.traceEnd();
+ log.traceEnd();
+ log.traceEnd();
+
+ verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L1"));
+ verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L2"));
+ verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L3"));
+ verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(3));
+
+ verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L2 took to complete: \\d+ms")));
+ verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L1 took to complete: \\d+ms")));
+ verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L3 took to complete: \\d+ms")),
+ never());
+
+ verify((MockedVoidMethod) () -> Slog.w(TAG, "not tracing duration of 'L3' "
+ + "because already reached 2 levels"));
+ }
+
+ @Test
+ public void testEndNoBegin() throws Exception {
+ TimingsTraceLog log = new TimingsTraceLog(TAG, TRACE_TAG_APP);
+ log.traceEnd();
+ verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP));
+ verify((MockedVoidMethod) () -> Slog.d(eq(TAG), anyString()), never());
+ verify((MockedVoidMethod) () -> Slog.w(TAG, "traceEnd called more times than traceBegin"));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index 1124220..4d7cf27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -33,6 +33,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.ApkAssets;
import android.os.PatternMatcher;
@@ -69,6 +70,8 @@
private static final String TAG = NavigationModeController.class.getSimpleName();
private static final boolean DEBUG = false;
+ private static final int SYSTEM_APP_MASK =
+ ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
static final String SHARED_PREFERENCES_NAME = "navigation_mode_controller_preferences";
static final String PREFS_SWITCHED_FROM_GESTURE_NAV_KEY = "switched_from_gesture_nav";
@@ -315,6 +318,10 @@
return;
}
+ Log.d(TAG, "Switching system navigation to 3-button mode:"
+ + " defaultLauncher=" + getDefaultLauncherPackageName(mCurrentUserContext)
+ + " contextUser=" + mCurrentUserContext.getUserId());
+
setModeOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, USER_CURRENT);
showNotification(mCurrentUserContext, R.string.notification_content_system_nav_changed);
mCurrentUserContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
@@ -355,9 +362,10 @@
if (defaultLauncherPackageName == null) {
return null;
}
- ComponentName recentsComponentName = ComponentName.unflattenFromString(
- context.getString(com.android.internal.R.string.config_recentsComponentName));
- return recentsComponentName.getPackageName().equals(defaultLauncherPackageName);
+ if (isSystemApp(context, defaultLauncherPackageName)) {
+ return true;
+ }
+ return false;
}
private String getDefaultLauncherPackageName(Context context) {
@@ -368,6 +376,17 @@
return cn.getPackageName();
}
+ /** Returns true if the app for the given package name is a system app for this device */
+ private boolean isSystemApp(Context context, String packageName) {
+ try {
+ ApplicationInfo ai = context.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_META_DATA);
+ return ai != null && ((ai.flags & SYSTEM_APP_MASK) != 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
private void showNotification(Context context, int resId) {
final CharSequence message = context.getResources().getString(resId);
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 186fd37..64a8ce0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2430,7 +2430,6 @@
onUnlockHintFinished();
return;
}
- mUpdateMonitor.requestFaceAuth();
super.startUnlockHintAnimation();
}
@@ -2651,10 +2650,14 @@
switch (mBarState) {
case StatusBarState.KEYGUARD:
if (!mDozingOnDown) {
- mLockscreenGestureLogger.write(
- MetricsEvent.ACTION_LS_HINT,
- 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
- startUnlockHintAnimation();
+ if (mKeyguardBypassController.getBypassEnabled()) {
+ mUpdateMonitor.requestFaceAuth();
+ } else {
+ mLockscreenGestureLogger.write(
+ MetricsEvent.ACTION_LS_HINT,
+ 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+ startUnlockHintAnimation();
+ }
}
return true;
case StatusBarState.SHADE_LOCKED:
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index d6657f1..8415272 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -31,6 +31,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IWindow;
@@ -83,6 +84,7 @@
mInteractionConnections = new SparseArray<>();
private final SparseArray<SparseArray<IBinder>> mWindowTokens = new SparseArray<>();
+ private final List<WindowInfo> mCachedWindowInfos = new ArrayList<>();
private List<AccessibilityWindowInfo> mWindows;
private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection;
@@ -173,25 +175,133 @@
}
/**
- * Callbacks from from window manager when there's an accessibility change in windows.
+ * Callbacks from window manager when there's an accessibility change in windows.
*
+ * @param forceSend Send the windows for accessibility even if they haven't changed.
* @param windows The windows of current display for accessibility.
*/
@Override
- public void onWindowsForAccessibilityChanged(@NonNull List<WindowInfo> windows) {
+ public void onWindowsForAccessibilityChanged(boolean forceSend,
+ @NonNull List<WindowInfo> windows) {
synchronized (mLock) {
if (DEBUG) {
Slog.i(LOG_TAG, "Windows changed: " + windows);
}
- // Let the policy update the focused and active windows.
- updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows);
-
- // Someone may be waiting for the windows - advertise it.
- mLock.notifyAll();
+ if (shouldUpdateWindowsLocked(forceSend, windows)) {
+ cacheWindows(windows);
+ // Let the policy update the focused and active windows.
+ updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows);
+ // Someone may be waiting for the windows - advertise it.
+ mLock.notifyAll();
+ }
}
}
+ private boolean shouldUpdateWindowsLocked(boolean forceSend,
+ @NonNull List<WindowInfo> windows) {
+ if (forceSend) {
+ return true;
+ }
+
+ final int windowCount = windows.size();
+ // We computed the windows and if they changed notify the client.
+ if (mCachedWindowInfos.size() != windowCount) {
+ // Different size means something changed.
+ return true;
+ } else if (!mCachedWindowInfos.isEmpty() || !windows.isEmpty()) {
+ // Since we always traverse windows from high to low layer
+ // the old and new windows at the same index should be the
+ // same, otherwise something changed.
+ for (int i = 0; i < windowCount; i++) {
+ WindowInfo oldWindow = mCachedWindowInfos.get(i);
+ WindowInfo newWindow = windows.get(i);
+ // We do not care for layer changes given the window
+ // order does not change. This brings no new information
+ // to the clients.
+ if (windowChangedNoLayer(oldWindow, newWindow)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void cacheWindows(List<WindowInfo> windows) {
+ final int oldWindowCount = mCachedWindowInfos.size();
+ for (int i = oldWindowCount - 1; i >= 0; i--) {
+ mCachedWindowInfos.remove(i).recycle();
+ }
+ final int newWindowCount = windows.size();
+ for (int i = 0; i < newWindowCount; i++) {
+ WindowInfo newWindow = windows.get(i);
+ mCachedWindowInfos.add(WindowInfo.obtain(newWindow));
+ }
+ }
+
+ private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
+ if (oldWindow == newWindow) {
+ return false;
+ }
+ if (oldWindow == null) {
+ return true;
+ }
+ if (newWindow == null) {
+ return true;
+ }
+ if (oldWindow.type != newWindow.type) {
+ return true;
+ }
+ if (oldWindow.focused != newWindow.focused) {
+ return true;
+ }
+ if (oldWindow.token == null) {
+ if (newWindow.token != null) {
+ return true;
+ }
+ } else if (!oldWindow.token.equals(newWindow.token)) {
+ return true;
+ }
+ if (oldWindow.parentToken == null) {
+ if (newWindow.parentToken != null) {
+ return true;
+ }
+ } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
+ return true;
+ }
+ if (oldWindow.activityToken == null) {
+ if (newWindow.activityToken != null) {
+ return true;
+ }
+ } else if (!oldWindow.activityToken.equals(newWindow.activityToken)) {
+ return true;
+ }
+ if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
+ return true;
+ }
+ if (oldWindow.childTokens != null && newWindow.childTokens != null
+ && !oldWindow.childTokens.equals(newWindow.childTokens)) {
+ return true;
+ }
+ if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
+ return true;
+ }
+ if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
+ return true;
+ }
+ if (oldWindow.inPictureInPicture != newWindow.inPictureInPicture) {
+ return true;
+ }
+ if (oldWindow.hasFlagWatchOutsideTouch != newWindow.hasFlagWatchOutsideTouch) {
+ return true;
+ }
+ if (oldWindow.displayId != newWindow.displayId) {
+ return true;
+ }
+ return false;
+ }
+
/**
* Start tracking windows changes from window manager.
*/
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 6eb9f0c..0748279 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IUidObserver;
@@ -193,7 +194,7 @@
// with other system events, any duration calculations should be done use startTime so as
// not to be affected by discontinuities created by RTC adjustments.
public final long startTimeDebug;
- public final int usageHint;
+ public final AudioAttributes attrs;
public final int uid;
public final String opPkg;
public final String reason;
@@ -206,12 +207,12 @@
public VibrationEffect originalEffect;
private Vibration(IBinder token, VibrationEffect effect,
- int usageHint, int uid, String opPkg, String reason) {
+ AudioAttributes attrs, int uid, String opPkg, String reason) {
this.token = token;
this.effect = effect;
this.startTime = SystemClock.elapsedRealtime();
this.startTimeDebug = System.currentTimeMillis();
- this.usageHint = usageHint;
+ this.attrs = attrs;
this.uid = uid;
this.opPkg = opPkg;
this.reason = reason;
@@ -231,7 +232,7 @@
}
public boolean isHapticFeedback() {
- if (VibratorService.this.isHapticFeedback(usageHint)) {
+ if (VibratorService.this.isHapticFeedback(attrs.getUsage())) {
return true;
}
if (effect instanceof VibrationEffect.Prebaked) {
@@ -256,15 +257,15 @@
}
public boolean isNotification() {
- return VibratorService.this.isNotification(usageHint);
+ return VibratorService.this.isNotification(attrs.getUsage());
}
public boolean isRingtone() {
- return VibratorService.this.isRingtone(usageHint);
+ return VibratorService.this.isRingtone(attrs.getUsage());
}
public boolean isAlarm() {
- return VibratorService.this.isAlarm(usageHint);
+ return VibratorService.this.isAlarm(attrs.getUsage());
}
public boolean isFromSystem() {
@@ -273,7 +274,7 @@
public VibrationInfo toInfo() {
return new VibrationInfo(
- startTimeDebug, effect, originalEffect, usageHint, uid, opPkg, reason);
+ startTimeDebug, effect, originalEffect, attrs, uid, opPkg, reason);
}
}
@@ -281,18 +282,18 @@
private final long mStartTimeDebug;
private final VibrationEffect mEffect;
private final VibrationEffect mOriginalEffect;
- private final int mUsageHint;
+ private final AudioAttributes mAttrs;
private final int mUid;
private final String mOpPkg;
private final String mReason;
public VibrationInfo(long startTimeDebug, VibrationEffect effect,
- VibrationEffect originalEffect, int usageHint, int uid,
+ VibrationEffect originalEffect, AudioAttributes attrs, int uid,
String opPkg, String reason) {
mStartTimeDebug = startTimeDebug;
mEffect = effect;
mOriginalEffect = originalEffect;
- mUsageHint = usageHint;
+ mAttrs = attrs;
mUid = uid;
mOpPkg = opPkg;
mReason = reason;
@@ -307,8 +308,8 @@
.append(mEffect)
.append(", originalEffect: ")
.append(mOriginalEffect)
- .append(", usageHint: ")
- .append(mUsageHint)
+ .append(", attrs: ")
+ .append(mAttrs)
.append(", uid: ")
.append(mUid)
.append(", opPkg: ")
@@ -549,12 +550,11 @@
}
@Override // Binder call
- public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, String reason,
- IBinder token) {
+ public void vibrate(int uid, String opPkg, VibrationEffect effect,
+ @Nullable AudioAttributes attrs, String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
try {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
- != PackageManager.PERMISSION_GRANTED) {
+ if (!hasPermission(android.Manifest.permission.VIBRATE)) {
throw new SecurityException("Requires VIBRATE permission");
}
if (token == null) {
@@ -566,6 +566,22 @@
return;
}
+ if (attrs == null) {
+ attrs = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_UNKNOWN)
+ .build();
+ }
+
+ if (shouldBypassDnd(attrs)) {
+ if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
+ final int flags = attrs.getAllFlags()
+ & ~AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+ attrs = new AudioAttributes.Builder(attrs).replaceFlags(flags).build();
+ }
+ }
+
// If our current vibration is longer than the new vibration and is the same amplitude,
// then just let the current one finish.
synchronized (mLock) {
@@ -608,13 +624,13 @@
return;
}
- Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg, reason);
+ Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
> ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
&& !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) {
Slog.e(TAG, "Ignoring incoming vibration as process with"
- + " uid = " + uid + " is background,"
- + " usage = " + AudioAttributes.usageToString(vib.usageHint));
+ + " uid= " + uid + " is background,"
+ + " attrs= " + vib.attrs);
return;
}
linkVibration(vib);
@@ -632,6 +648,11 @@
}
}
+ private boolean hasPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
private static boolean isRepeatingVibration(VibrationEffect effect) {
return effect.getDuration() == Long.MAX_VALUE;
}
@@ -760,14 +781,14 @@
if (vib.effect instanceof VibrationEffect.OneShot) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
- doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
+ doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.attrs);
mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
} else if (vib.effect instanceof VibrationEffect.Waveform) {
// mThread better be null here. doCancelVibrate should always be
// called before startNextVibrationLocked or startVibrationLocked.
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
- mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
+ mThread = new VibrateThread(waveform, vib.uid, vib.attrs);
mThread.start();
} else if (vib.effect instanceof VibrationEffect.Prebaked) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
@@ -788,13 +809,14 @@
return true;
}
- if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
+ if (vib.attrs.getUsage() == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
return true;
}
- if (vib.usageHint == AudioAttributes.USAGE_ALARM ||
- vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
- vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
+ if (vib.attrs.getUsage() == AudioAttributes.USAGE_ALARM
+ || vib.attrs.getUsage() == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
+ || vib.attrs.getUsage()
+ == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
return true;
}
@@ -887,12 +909,24 @@
}
}
+ private static boolean shouldBypassDnd(AudioAttributes attrs) {
+ return (attrs.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
+ }
+
private int getAppOpMode(Vibration vib) {
int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
- vib.usageHint, vib.uid, vib.opPkg);
+ vib.attrs.getUsage(), vib.uid, vib.opPkg);
if (mode == AppOpsManager.MODE_ALLOWED) {
mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
}
+
+ if (mode == AppOpsManager.MODE_IGNORED && shouldBypassDnd(vib.attrs)) {
+ // If we're just ignoring the vibration op then this is set by DND and we should ignore
+ // if we're asked to bypass. AppOps won't be able to record this operation, so make
+ // sure we at least note it in the logs for debugging.
+ Slog.d(TAG, "Bypassing DND for vibration: " + vib);
+ mode = AppOpsManager.MODE_ALLOWED;
+ }
return mode;
}
@@ -1032,7 +1066,7 @@
return vibratorExists();
}
- private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
+ private void doVibratorOn(long millis, int amplitude, int uid, AudioAttributes attrs) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
try {
synchronized (mInputDeviceVibrators) {
@@ -1046,10 +1080,8 @@
noteVibratorOnLocked(uid, millis);
final int vibratorCount = mInputDeviceVibrators.size();
if (vibratorCount != 0) {
- final AudioAttributes attributes =
- new AudioAttributes.Builder().setUsage(usageHint).build();
for (int i = 0; i < vibratorCount; i++) {
- mInputDeviceVibrators.get(i).vibrate(millis, attributes);
+ mInputDeviceVibrators.get(i).vibrate(millis, attrs);
}
} else {
// Note: ordering is important here! Many haptic drivers will reset their
@@ -1118,7 +1150,7 @@
Slog.w(TAG, "Failed to play prebaked effect, no fallback");
return 0;
}
- Vibration fallbackVib = new Vibration(vib.token, effect, vib.usageHint, vib.uid,
+ Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid,
vib.opPkg, vib.reason + " (fallback)");
final int intensity = getCurrentIntensityLocked(fallbackVib);
linkVibration(fallbackVib);
@@ -1213,14 +1245,14 @@
private class VibrateThread extends Thread {
private final VibrationEffect.Waveform mWaveform;
private final int mUid;
- private final int mUsageHint;
+ private final AudioAttributes mAttrs;
private boolean mForceStop;
- VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) {
+ VibrateThread(VibrationEffect.Waveform waveform, int uid, AudioAttributes attrs) {
mWaveform = waveform;
mUid = uid;
- mUsageHint = usageHint;
+ mAttrs = attrs;
mTmpWorkSource.set(uid);
mWakeLock.setWorkSource(mTmpWorkSource);
}
@@ -1295,7 +1327,7 @@
// appropriate intervals.
onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
repeat);
- doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
+ doVibratorOn(onDuration, amplitude, mUid, mAttrs);
} else {
doVibratorSetAmplitude(amplitude);
}
@@ -1612,8 +1644,9 @@
VibrationEffect effect =
VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
- vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
- "Shell Command", mToken);
+ AudioAttributes attrs = createAudioAttributes(commonOptions);
+ vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
+ mToken);
return 0;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -1672,8 +1705,9 @@
amplitudesList.stream().mapToInt(Integer::intValue).toArray();
effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
}
- vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
- "Shell Command", mToken);
+ AudioAttributes attrs = createAudioAttributes(commonOptions);
+ vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
+ mToken);
return 0;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -1703,14 +1737,25 @@
VibrationEffect effect =
VibrationEffect.get(id, false);
- vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
- "Shell Command", mToken);
+ AudioAttributes attrs = createAudioAttributes(commonOptions);
+ vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
+ mToken);
return 0;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
+ private AudioAttributes createAudioAttributes(CommonOptions commonOptions) {
+ final int flags = commonOptions.force
+ ? AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
+ : 0;
+ return new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_UNKNOWN)
+ .setFlags(flags)
+ .build();
+ }
+
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter();) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f451a09..4222fc1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -272,6 +272,7 @@
import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.provider.DeviceConfig.Properties;
import android.server.ServerProtoEnums;
import android.sysprop.VoldProperties;
import android.text.TextUtils;
@@ -356,6 +357,7 @@
import com.android.server.utils.PriorityDump;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.ActivityMetricsLaunchObserver;
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerService;
@@ -392,6 +394,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
@@ -861,6 +864,51 @@
*/
final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
+ /**
+ * Depth of overlapping activity-start PSS deferral notes
+ */
+ private final AtomicInteger mActivityStartingNesting = new AtomicInteger(0);
+
+ private final ActivityMetricsLaunchObserver mActivityLaunchObserver =
+ new ActivityMetricsLaunchObserver() {
+ @Override
+ public void onActivityLaunched(byte[] activity, int temperature) {
+ // This is safe to force to the head of the queue because it relies only
+ // on refcounting to track begin/end of deferrals, not on actual
+ // message ordering. We don't care *what* activity is being
+ // launched; only that we're doing so.
+ if (mPssDeferralTime > 0) {
+ final Message msg = mBgHandler.obtainMessage(DEFER_PSS_MSG);
+ mBgHandler.sendMessageAtFrontOfQueue(msg);
+ }
+ }
+
+ // The other observer methods are unused
+ @Override
+ public void onIntentStarted(Intent intent) {
+ }
+
+ @Override
+ public void onIntentFailed() {
+ }
+
+ @Override
+ public void onActivityLaunchCancelled(byte[] abortingActivity) {
+ }
+
+ @Override
+ public void onActivityLaunchFinished(byte[] finalActivity) {
+ }
+ };
+
+ /**
+ * How long we defer PSS gathering while activities are starting, in milliseconds.
+ * This is adjustable via DeviceConfig. If it is zero or negative, no PSS deferral
+ * is done.
+ */
+ private volatile long mPssDeferralTime = 0;
+ private static final String ACTIVITY_START_PSS_DEFER_CONFIG = "activity_start_pss_defer";
+
private boolean mBinderTransactionTrackingEnabled = false;
/**
@@ -874,6 +922,20 @@
*/
boolean mFullPssPending = false;
+ /**
+ * Observe DeviceConfig changes to the PSS calculation interval
+ */
+ private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(Properties properties) {
+ mPssDeferralTime = properties.getLong(ACTIVITY_START_PSS_DEFER_CONFIG, 0);
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Activity-start PSS delay now "
+ + mPssDeferralTime + " ms");
+ }
+ }
+ };
/**
* This is for verifying the UID report flow.
@@ -1838,6 +1900,8 @@
}
static final int COLLECT_PSS_BG_MSG = 1;
+ static final int DEFER_PSS_MSG = 2;
+ static final int STOP_DEFERRING_PSS_MSG = 3;
final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
@Override
@@ -1945,6 +2009,30 @@
}
} while (true);
}
+
+ case DEFER_PSS_MSG: {
+ deferPssForActivityStart();
+ } break;
+
+ case STOP_DEFERRING_PSS_MSG: {
+ final int nesting = mActivityStartingNesting.decrementAndGet();
+ if (nesting <= 0) {
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "PSS activity start deferral interval ended; now "
+ + nesting);
+ }
+ if (nesting < 0) {
+ Slog.wtf(TAG, "Activity start nesting undercount!");
+ mActivityStartingNesting.incrementAndGet();
+ }
+ } else {
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Still deferring PSS, nesting=" + nesting);
+ }
+ }
+ }
+ break;
+
}
}
};
@@ -8832,6 +8920,12 @@
NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
mHiddenApiBlacklist.registerObserver();
+ final long pssDeferralMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ ACTIVITY_START_PSS_DEFER_CONFIG, 0L);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ ActivityThread.currentApplication().getMainExecutor(),
+ mPssDelayConfigListener);
+
synchronized (this) {
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
@@ -8848,6 +8942,7 @@
com.android.internal.R.bool.config_multiuserDelayUserDataLocking);
mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
+ mPssDeferralTime = pssDeferralMs;
}
}
@@ -8921,6 +9016,10 @@
mAtmInternal.updateTopComponentForFactoryTest();
t.traceEnd();
+ t.traceBegin("registerActivityLaunchObserver");
+ mAtmInternal.getLaunchObserverRegistry().registerLaunchObserver(mActivityLaunchObserver);
+ t.traceEnd();
+
t.traceBegin("watchDeviceProvisioning");
watchDeviceProvisioning(mContext);
t.traceEnd();
@@ -16177,7 +16276,13 @@
return false;
}
if (mPendingPssProcesses.size() == 0) {
- mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+ final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0)
+ ? mPssDeferralTime : 0;
+ if (DEBUG_PSS && deferral > 0) {
+ Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by "
+ + deferral + " ms");
+ }
+ mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, deferral);
}
if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc);
proc.pssProcState = procState;
@@ -16187,6 +16292,30 @@
}
/**
+ * Re-defer a posted PSS collection pass, if one exists. Assumes deferral is
+ * currently active policy when called.
+ */
+ private void deferPssIfNeededLocked() {
+ if (mPendingPssProcesses.size() > 0) {
+ mBgHandler.removeMessages(COLLECT_PSS_BG_MSG);
+ mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, mPssDeferralTime);
+ }
+ }
+
+ private void deferPssForActivityStart() {
+ synchronized (ActivityManagerService.this) {
+ if (mPssDeferralTime > 0) {
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Deferring PSS collection for activity start");
+ }
+ deferPssIfNeededLocked();
+ mActivityStartingNesting.getAndIncrement();
+ mBgHandler.sendEmptyMessageDelayed(STOP_DEFERRING_PSS_MSG, mPssDeferralTime);
+ }
+ }
+ }
+
+ /**
* Schedule PSS collection of all processes.
*/
void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 6b7187e..a2891e9 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -43,9 +43,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
@@ -1036,12 +1034,9 @@
private static final boolean DEBUG = false;
- private final SparseArray<WindowState> mTempWindowStates =
- new SparseArray<WindowState>();
+ private final SparseArray<WindowState> mTempWindowStates = new SparseArray<>();
- private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
-
- private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
+ private final Set<IBinder> mTempBinderSet = new ArraySet<>();
private final RectF mTempRectF = new RectF();
@@ -1098,8 +1093,7 @@
Slog.i(LOG_TAG, "computeChangedWindows()");
}
- boolean windowsChanged = false;
- List<WindowInfo> windows = new ArrayList<WindowInfo>();
+ List<WindowInfo> windows = new ArrayList<>();
synchronized (mService.mGlobalLock) {
// Do not send the windows if there is no current focus as
@@ -1169,46 +1163,9 @@
visibleWindows.clear();
addedWindows.clear();
-
- if (!forceSend) {
- // We computed the windows and if they changed notify the client.
- if (mOldWindows.size() != windows.size()) {
- // Different size means something changed.
- windowsChanged = true;
- } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
- // Since we always traverse windows from high to low layer
- // the old and new windows at the same index should be the
- // same, otherwise something changed.
- for (int i = 0; i < windowCount; i++) {
- WindowInfo oldWindow = mOldWindows.get(i);
- WindowInfo newWindow = windows.get(i);
- // We do not care for layer changes given the window
- // order does not change. This brings no new information
- // to the clients.
- if (windowChangedNoLayer(oldWindow, newWindow)) {
- windowsChanged = true;
- break;
- }
- }
- }
- }
-
- if (forceSend || windowsChanged) {
- cacheWindows(windows);
- }
}
- // Now we do not hold the lock, so send the windows over.
- if (forceSend || windowsChanged) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Windows changed or force sending:" + windows);
- }
- mCallback.onWindowsForAccessibilityChanged(windows);
- } else {
- if (DEBUG) {
- Log.i(LOG_TAG, "No windows changed.");
- }
- }
+ mCallback.onWindowsForAccessibilityChanged(forceSend, windows);
// Recycle the windows as we do not need them.
clearAndRecycleWindows(windows);
@@ -1313,67 +1270,6 @@
tokenOut.add(window.token);
}
- private void cacheWindows(List<WindowInfo> windows) {
- final int oldWindowCount = mOldWindows.size();
- for (int i = oldWindowCount - 1; i >= 0; i--) {
- mOldWindows.remove(i).recycle();
- }
- final int newWindowCount = windows.size();
- for (int i = 0; i < newWindowCount; i++) {
- WindowInfo newWindow = windows.get(i);
- mOldWindows.add(WindowInfo.obtain(newWindow));
- }
- }
-
- private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
- if (oldWindow == newWindow) {
- return false;
- }
- if (oldWindow == null) {
- return true;
- }
- if (newWindow == null) {
- return true;
- }
- if (oldWindow.type != newWindow.type) {
- return true;
- }
- if (oldWindow.focused != newWindow.focused) {
- return true;
- }
- if (oldWindow.token == null) {
- if (newWindow.token != null) {
- return true;
- }
- } else if (!oldWindow.token.equals(newWindow.token)) {
- return true;
- }
- if (oldWindow.parentToken == null) {
- if (newWindow.parentToken != null) {
- return true;
- }
- } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
- return true;
- }
- if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
- return true;
- }
- if (oldWindow.childTokens != null && newWindow.childTokens != null
- && !oldWindow.childTokens.equals(newWindow.childTokens)) {
- return true;
- }
- if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
- return true;
- }
- if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
- return true;
- }
- if (oldWindow.displayId != newWindow.displayId) {
- return true;
- }
- return false;
- }
-
private static void clearAndRecycleWindows(List<WindowInfo> windows) {
final int windowCount = windows.size();
for (int i = windowCount - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index deae923..4a9a3f7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -79,7 +79,6 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
-import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
@@ -541,14 +540,6 @@
// If the app was already visible, don't reset the waitingToShow state.
if (isHidden()) {
waitingToShow = true;
-
- // Let's reset the draw state in order to prevent the starting window to be
- // immediately dismissed when the app still has the surface.
- forAllWindows(w -> {
- if (w.mWinAnimator.mDrawState == HAS_DRAWN) {
- w.mWinAnimator.resetDrawState();
- }
- }, true /* traverseTopToBottom */);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 40bec14..6910ce9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -51,9 +51,10 @@
/**
* Called when the windows for accessibility changed.
*
+ * @param forceSend Send the windows for accessibility even if they haven't changed.
* @param windows The windows for accessibility.
*/
- public void onWindowsForAccessibilityChanged(List<WindowInfo> windows);
+ void onWindowsForAccessibilityChanged(boolean forceSend, List<WindowInfo> windows);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 264eeda..2bf24f6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7694,7 +7694,7 @@
private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
final WindowState touchedWindow = windowForClientLocked(null, touchedToken, false);
- if (touchedWindow == null) {
+ if (touchedWindow == null || !touchedWindow.canReceiveKeys()) {
return;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 04ca40e..22408cc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -70,6 +70,8 @@
*/
public class AccessibilityWindowManagerTest {
private static final String PACKAGE_NAME = "com.android.server.accessibility";
+ private static final boolean FORCE_SEND = true;
+ private static final boolean SEND_ON_WINDOW_CHANGES = false;
private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM;
private static final int NUM_GLOBAL_WINDOWS = 4;
private static final int NUM_APP_WINDOWS = 4;
@@ -122,7 +124,7 @@
mWindowInfos.get(DEFAULT_FOCUSED_INDEX).focused = true;
// Turn on windows tracking, and update window info
mA11yWindowManager.startTrackingWindows();
- mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+ mA11yWindowManager.onWindowsForAccessibilityChanged(FORCE_SEND, mWindowInfos);
assertEquals(mA11yWindowManager.getWindowListLocked().size(),
mWindowInfos.size());
@@ -169,16 +171,16 @@
@Test
public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() {
final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
- WindowInfo focuedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX);
+ WindowInfo focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX);
assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, focuedWindowInfo.token));
+ USER_SYSTEM_ID, focusedWindowInfo.token));
- focuedWindowInfo.focused = false;
- focuedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX + 1);
- focuedWindowInfo.focused = true;
+ focusedWindowInfo.focused = false;
+ focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX + 1);
+ focusedWindowInfo.focused = true;
mA11yWindowManager.onTouchInteractionStart();
- mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+ mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
}
@@ -208,6 +210,52 @@
}
@Test
+ public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
+ final WindowInfo windowInfo = mWindowInfos.get(0);
+ final int correctLayer = mA11yWindowManager.getWindowListLocked().get(0).getLayer();
+ windowInfo.layer += 1;
+
+ mA11yWindowManager.onWindowsForAccessibilityChanged(FORCE_SEND, mWindowInfos);
+ assertNotEquals(correctLayer, mA11yWindowManager.getWindowListLocked().get(0).getLayer());
+ }
+
+ @Test
+ public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() {
+ final WindowInfo windowInfo = mWindowInfos.get(0);
+ final int correctLayer = mA11yWindowManager.getWindowListLocked().get(0).getLayer();
+ windowInfo.layer += 1;
+
+ mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+ assertEquals(correctLayer, mA11yWindowManager.getWindowListLocked().get(0).getLayer());
+ }
+
+ @Test
+ public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows()
+ throws RemoteException {
+ final AccessibilityWindowInfo oldWindow = mA11yWindowManager.getWindowListLocked().get(0);
+ final IWindow token = addAccessibilityInteractionConnection(true);
+ final WindowInfo windowInfo = WindowInfo.obtain();
+ windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
+ windowInfo.token = token.asBinder();
+ windowInfo.layer = 0;
+ windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ mWindowInfos.set(0, windowInfo);
+
+ mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+ assertNotEquals(oldWindow, mA11yWindowManager.getWindowListLocked().get(0));
+ }
+
+ @Test
+ public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() {
+ final WindowInfo focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX);
+ final WindowInfo windowInfo = mWindowInfos.get(0);
+ focusedWindowInfo.focused = false;
+ windowInfo.focused = true;
+ mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+ assertTrue(mA11yWindowManager.getWindowListLocked().get(0).isFocused());
+ }
+
+ @Test
public void removeAccessibilityInteractionConnection_byWindowToken_shouldRemoved() {
for (int i = 0; i < NUM_OF_WINDOWS; i++) {
final int windowId = mA11yWindowTokens.keyAt(i);
@@ -264,7 +312,7 @@
windowInfo = mWindowInfos.get(1);
windowInfo.boundsInScreen.set(0, SCREEN_HEIGHT / 2,
SCREEN_WIDTH, SCREEN_HEIGHT);
- mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+ mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
final Region outBounds = new Region();
@@ -291,7 +339,7 @@
windowInfo = mWindowInfos.get(1);
windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+ mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
final Region outBounds = new Region();
int windowId = a11yWindows.get(1).getId();
@@ -309,7 +357,7 @@
windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
windowInfo = mWindowInfos.get(1);
windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+ mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
final Region outBounds = new Region();
@@ -498,7 +546,7 @@
public void getPictureInPictureWindow_shouldNotNull() {
assertNull(mA11yWindowManager.getPictureInPictureWindow());
mWindowInfos.get(1).inPictureInPicture = true;
- mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+ mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
assertNotNull(mA11yWindowManager.getPictureInPictureWindow());
}
@@ -511,7 +559,7 @@
mA11yWindowManager.getConnectionLocked(
USER_SYSTEM_ID, outsideWindowId).getRemote();
mWindowInfos.get(0).hasFlagWatchOutsideTouch = true;
- mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+ mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId);
verify(mockRemoteConnection).notifyOutsideTouch();
diff --git a/startop/scripts/app_startup/run_app_with_prefetch b/startop/scripts/app_startup/run_app_with_prefetch
index 643df1b..92a31c3 100755
--- a/startop/scripts/app_startup/run_app_with_prefetch
+++ b/startop/scripts/app_startup/run_app_with_prefetch
@@ -101,6 +101,15 @@
esac
shift
done
+
+ if [[ $when == "aot" ]]; then
+ # TODO: re-implement aot later for experimenting.
+ echo "Error: --when $when is unsupported" >&2
+ exit 1
+ elif [[ $when != "jit" ]]; then
+ echo "Error: --when must be one of (aot jit)." >&2
+ exit 1
+ fi
}
echo_to_output_file() {
@@ -212,6 +221,12 @@
local the_when="$1" # user: aot, jit
local the_mode="$2" # warm, cold, fadvise, mlock, etc.
+ # iorapd readahead for jit+(mlock/fadvise)
+ if [[ $the_when == "jit" && $the_mode != 'warm' && $the_mode != 'cold' ]]; then
+ iorapd_readahead_enable
+ return 0
+ fi
+
if [[ $the_when != "aot" ]]; then
# TODO: just in time implementation.. should probably use system server.
return 0
@@ -250,13 +265,18 @@
local the_when="$1" # user: aot, jit
local the_mode="$2" # warm, cold, fadvise, mlock, etc.
local logcat_timestamp="$3" # timestamp from before am start.
+ local res
if [[ $the_when != "aot" ]]; then
if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then
# Validate that readahead completes.
# If this fails for some reason, then this will also discard the timing of the run.
iorapd_readahead_wait_until_finished "$package" "$activity" "$logcat_timestamp" "$timeout"
- return $?
+ res=$?
+
+ iorapd_readahead_disable
+
+ return $res
fi
# Don't need to do anything for warm or cold.
return 0
diff --git a/startop/scripts/iorap/collector b/startop/scripts/iorap/collector
index d96125f..3dc080a 100755
--- a/startop/scripts/iorap/collector
+++ b/startop/scripts/iorap/collector
@@ -322,6 +322,7 @@
iorapd_compiler_purge_trace_file "$package" "$activity" || return $?
iorapd_perfetto_enable || return $?
+ iorapd_readahead_disable || return $?
iorapd_start || return $?
# Wait for perfetto trace to finished writing itself out.
diff --git a/startop/scripts/iorap/common b/startop/scripts/iorap/common
index cb2b618..031dabf 100755
--- a/startop/scripts/iorap/common
+++ b/startop/scripts/iorap/common
@@ -45,7 +45,7 @@
iorapd_reset # iorapd only reads this flag when initializing
}
-# Enable perfetto tracing.
+# Disable perfetto tracing.
# Subsequent launches of applications will no longer record perfetto trace protobufs.
iorapd_perfetto_disable() {
verbose_print 'disable perfetto'
@@ -53,6 +53,31 @@
iorapd_reset # iorapd only reads this flag when initializing
}
+# Enable readahead
+# Subsequent launches of an application will be sped up by iorapd readahead prefetching
+# (Provided an appropriate compiled trace exists for that application)
+iorapd_readahead_enable() {
+ if [[ "$(adb shell getprop iorapd.readahead.enable)" == true ]]; then
+ verbose_print 'enable readahead [already enabled]'
+ return 0
+ fi
+ verbose_print 'enable readahead [reset iorapd]'
+ adb shell setprop iorapd.readahead.enable true
+ iorapd_reset # iorapd only reads this flag when initializing
+}
+
+# Disable readahead
+# Subsequent launches of an application will be not be sped up by iorapd readahead prefetching.
+iorapd_readahead_disable() {
+ if [[ "$(adb shell getprop iorapd.readahead.enable)" == false ]]; then
+ verbose_print 'disable readahead [already disabled]'
+ return 0
+ fi
+ verbose_print 'disable readahead [reset iorapd]'
+ adb shell setprop iorapd.readahead.enable false
+ iorapd_reset # iorapd only reads this flag when initializing
+}
+
_iorapd_path_to_data_file() {
local package="$1"
local activity="$2"
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
index 388c7d0..c50229a 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
@@ -16,9 +16,7 @@
package com.android.framework.permission.tests;
-import junit.framework.TestCase;
-
-import android.media.AudioManager;
+import android.media.AudioAttributes;
import android.os.Binder;
import android.os.IVibratorService;
import android.os.Process;
@@ -27,6 +25,9 @@
import android.os.VibrationEffect;
import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+
/**
* Verify that Hardware apis cannot be called without required permissions.
*/
@@ -51,7 +52,10 @@
try {
final VibrationEffect effect =
VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
- mVibratorService.vibrate(Process.myUid(), null, effect, AudioManager.STREAM_ALARM,
+ final AudioAttributes attrs = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ALARM)
+ .build();
+ mVibratorService.vibrate(Process.myUid(), null, effect, attrs,
"testVibrate", new Binder());
fail("vibrate did not throw SecurityException as expected");
} catch (SecurityException e) {