Merge "Fix crash in IncidentCompanionService. Null means *don't* do the check."
diff --git a/api/current.txt b/api/current.txt
index 1e6bf2c..8794780 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23279,6 +23279,7 @@
method @Deprecated public boolean registerRemoteController(android.media.RemoteController);
method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest);
+ method public void setAllowedCapturePolicy(int);
method @Deprecated public void setBluetoothA2dpOn(boolean);
method public void setBluetoothScoOn(boolean);
method public void setMicrophoneMute(boolean);
@@ -52311,7 +52312,7 @@
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getActionList();
method @Deprecated public int getActions();
method public java.util.List<java.lang.String> getAvailableExtraData();
- method public void getBoundsInParent(android.graphics.Rect);
+ method @Deprecated public void getBoundsInParent(android.graphics.Rect);
method public void getBoundsInScreen(android.graphics.Rect);
method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
method public int getChildCount();
@@ -52380,7 +52381,7 @@
method public boolean removeChild(android.view.View, int);
method public void setAccessibilityFocused(boolean);
method public void setAvailableExtraData(java.util.List<java.lang.String>);
- method public void setBoundsInParent(android.graphics.Rect);
+ method @Deprecated public void setBoundsInParent(android.graphics.Rect);
method public void setBoundsInScreen(android.graphics.Rect);
method public void setCanOpenPopup(boolean);
method public void setCheckable(boolean);
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 685c067..b46c9e3 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -31,7 +31,7 @@
namespace incidentd {
const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB
-const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
+const ssize_t MAX_BUFFER_COUNT = 1536; // 24 MB max
FdBuffer::FdBuffer()
:mBuffer(new EncodedBuffer(BUFFER_SIZE)),
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 385d491..774a359 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -1682,17 +1682,29 @@
}
/**
- * Gets the node bounds in parent coordinates.
+ * Gets the node bounds in the viewParent's coordinates.
+ * {@link #getParent()} does not represent the source's viewParent.
+ * Instead it represents the result of {@link View#getParentForAccessibility()},
+ * which returns the closest ancestor where {@link View#isImportantForAccessibility()} is true.
+ * So this method is not reliable.
*
* @param outBounds The output node bounds.
+ * @deprecated Use {@link #getBoundsInScreen(Rect)} instead.
+ *
*/
+ @Deprecated
public void getBoundsInParent(Rect outBounds) {
outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
mBoundsInParent.right, mBoundsInParent.bottom);
}
/**
- * Sets the node bounds in parent coordinates.
+ * Sets the node bounds in the viewParent's coordinates.
+ * {@link #getParent()} does not represent the source's viewParent.
+ * Instead it represents the result of {@link View#getParentForAccessibility()},
+ * which returns the closest ancestor where {@link View#isImportantForAccessibility()} is true.
+ * So this method is not reliable.
+ *
* <p>
* <strong>Note:</strong> Cannot be called from an
* {@link android.accessibilityservice.AccessibilityService}.
@@ -1702,7 +1714,9 @@
* @param bounds The node bounds.
*
* @throws IllegalStateException If called from an AccessibilityService.
+ * @deprecated Accessibility services should not care about these bounds.
*/
+ @Deprecated
public void setBoundsInParent(Rect bounds) {
enforceNotSealed();
mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 88713d1..a296d64 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2229,6 +2229,11 @@
return AudioSystem::isHapticPlaybackSupported();
}
+static jint
+android_media_AudioSystem_setAllowedCapturePolicy(JNIEnv *env, jobject thiz, jint uid, jint flags) {
+ return AudioSystem::setAllowedCapturePolicy(uid, flags);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -2304,6 +2309,7 @@
{"isHapticPlaybackSupported", "()Z", (void *)android_media_AudioSystem_isHapticPlaybackSupported},
{"getHwOffloadEncodingFormatsSupportedForA2DP", "(Ljava/util/ArrayList;)I",
(void*)android_media_AudioSystem_getHwOffloadEncodingFormatsSupportedForA2DP},
+ {"setAllowedCapturePolicy", "(II)I", (void *)android_media_AudioSystem_setAllowedCapturePolicy},
};
static const JNINativeMethod gEventHandlerMethods[] = {
diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp
new file mode 100644
index 0000000..e9b22c1
--- /dev/null
+++ b/keystore/tests/Android.bp
@@ -0,0 +1,26 @@
+// 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.
+
+android_test {
+ name: "KeystoreTests",
+ // LOCAL_MODULE := keystore
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "hamcrest-library",
+ ],
+ platform_apis: true,
+ libs: ["android.test.runner"],
+ certificate: "platform",
+}
diff --git a/location/Android.mk b/location/Android.mk
deleted file mode 100644
index 50509c6..0000000
--- a/location/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright (C) 2010 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(call all-subdir-makefiles, $(LOCAL_PATH))
\ No newline at end of file
diff --git a/location/tests/Android.bp b/location/tests/Android.bp
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/location/tests/Android.bp
@@ -0,0 +1 @@
+
diff --git a/location/tests/Android.mk b/location/tests/Android.mk
deleted file mode 100644
index 57848f3..0000000
--- a/location/tests/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
\ No newline at end of file
diff --git a/location/tests/locationtests/Android.bp b/location/tests/locationtests/Android.bp
new file mode 100644
index 0000000..1a4e2c7
--- /dev/null
+++ b/location/tests/locationtests/Android.bp
@@ -0,0 +1,19 @@
+android_test {
+ name: "FrameworksLocationTests",
+ // Include all test java files.
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ platform_apis: true,
+ static_libs: [
+ "androidx.test.rules",
+ "core-test-rules",
+ "guava",
+ "mockito-target-minus-junit4",
+ "frameworks-base-testutils",
+ "truth-prebuilt",
+ ],
+ test_suites: ["device-tests"],
+}
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
deleted file mode 100644
index 3dcf694..0000000
--- a/location/tests/locationtests/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_PACKAGE_NAME := FrameworksLocationTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.rules \
- core-test-rules \
- guava \
- mockito-target-minus-junit4 \
- frameworks-base-testutils \
- truth-prebuilt \
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-include $(BUILD_PACKAGE)
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index e925731..a3eee0a 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -710,20 +710,7 @@
* @throws IllegalArgumentException if the argument is not a valid value.
*/
public @NonNull Builder setAllowedCapturePolicy(@CapturePolicy int capturePolicy) {
- switch (capturePolicy) {
- case ALLOW_CAPTURE_BY_NONE:
- mFlags |= FLAG_NO_MEDIA_PROJECTION | FLAG_NO_SYSTEM_CAPTURE;
- break;
- case ALLOW_CAPTURE_BY_SYSTEM:
- mFlags |= FLAG_NO_MEDIA_PROJECTION;
- mFlags &= ~FLAG_NO_SYSTEM_CAPTURE;
- break;
- case ALLOW_CAPTURE_BY_ALL:
- mFlags &= ~FLAG_NO_SYSTEM_CAPTURE & ~FLAG_NO_MEDIA_PROJECTION;
- break;
- default:
- throw new IllegalArgumentException("Unknown allow playback capture policy");
- }
+ mFlags = capturePolicyToFlags(capturePolicy, mFlags);
return this;
}
@@ -1219,6 +1206,24 @@
}
}
+ static int capturePolicyToFlags(@CapturePolicy int capturePolicy, int flags) {
+ switch (capturePolicy) {
+ case ALLOW_CAPTURE_BY_NONE:
+ flags |= FLAG_NO_MEDIA_PROJECTION | FLAG_NO_SYSTEM_CAPTURE;
+ break;
+ case ALLOW_CAPTURE_BY_SYSTEM:
+ flags |= FLAG_NO_MEDIA_PROJECTION;
+ flags &= ~FLAG_NO_SYSTEM_CAPTURE;
+ break;
+ case ALLOW_CAPTURE_BY_ALL:
+ flags &= ~FLAG_NO_SYSTEM_CAPTURE & ~FLAG_NO_MEDIA_PROJECTION;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown allow playback capture policy");
+ }
+ return flags;
+ }
+
/** @hide */
@IntDef({
USAGE_UNKNOWN,
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7cb5e00..dc5c663 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1478,6 +1478,30 @@
}
}
+ /**
+ * Specifying if this audio may or may not be captured by other apps or the system.
+ *
+ * The default is {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL}.
+ *
+ * Note that each audio track can also set its policy, in which case the most
+ * restrictive policy is always applied.
+ *
+ * @param capturePolicy one of
+ * {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL},
+ * {@link AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM},
+ * {@link AudioAttributes#ALLOW_CAPTURE_BY_NONE}.
+ * @throws IllegalArgumentException if the argument is not a valid value.
+ */
+ public void setAllowedCapturePolicy(@AudioAttributes.CapturePolicy int capturePolicy) {
+ int flags = AudioAttributes.capturePolicyToFlags(capturePolicy, 0x0);
+ // TODO: got trough AudioService and save a cache to restore in case of AP crash
+ // TODO: also pass the package in case multiple packages have the same UID
+ int result = AudioSystem.setAllowedCapturePolicy(Process.myUid(), flags);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ Log.e(TAG, "Could not setAllowedCapturePolicy: " + result);
+ }
+ }
+
//====================================================================
// Offload query
/**
diff --git a/media/java/android/media/AudioPlaybackCaptureConfiguration.java b/media/java/android/media/AudioPlaybackCaptureConfiguration.java
index 9ee6f87..bcaef03 100644
--- a/media/java/android/media/AudioPlaybackCaptureConfiguration.java
+++ b/media/java/android/media/AudioPlaybackCaptureConfiguration.java
@@ -29,13 +29,17 @@
* Configuration for capturing audio played by other apps.
*
* Only the following audio can be captured:
- * - usage MUST be UNKNOWN or GAME or MEDIA. All other usages CAN NOT be capturable.
- * - audio attributes MUST NOT have the FLAG_NO_CAPTURE
+ * - usage MUST be {@link AudioAttributes#USAGE_UNKNOWN} or {@link AudioAttributes#USAGE_GAME}
+ * or {@link AudioAttributes#USAGE_MEDIA}. All other usages CAN NOT be captured.
+ * - audio attributes MUST have its ${@link AudioAttributes.Builder#setAllowedCapturePolicy}
+ * to {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL}.
* - played by apps that MUST be in the same user profile as the capturing app
* (eg work profile can not capture user profile apps and vice-versa).
- * - played by apps that MUST NOT have in their manifest.xml the application
- * attribute android:allowAudioPlaybackCapture="false"
- * - played by apps that MUST have a targetSdkVersion higher or equal to 29 (Q).
+ * - played by apps for which the attribute allowAudioPlaybackCapture in their manifest
+ * MUST either be:
+ * * set to "true"
+ * * not set, and their targetSdkVersion MUST be equal or higher to
+ * {@link android.os.Build.VERSION_CODES#Q}.
*
* <p>An example for creating a capture configuration for capturing all media playback:
*
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index a7760a80..2dd7f0f 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -25,6 +25,7 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
+import android.media.MediaRecorder.Source;
import android.media.audiopolicy.AudioMix;
import android.media.audiopolicy.AudioPolicy;
import android.media.projection.MediaProjection;
@@ -539,7 +540,7 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- public Builder setAudioSource(int source) throws IllegalArgumentException {
+ public Builder setAudioSource(@Source int source) throws IllegalArgumentException {
Preconditions.checkState(
mAudioPlaybackCaptureConfiguration == null,
ERROR_MESSAGE_SOURCE_MISMATCH);
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index ad255fe..d105fa3 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1022,6 +1022,11 @@
public static native float getStreamVolumeDB(int stream, int index, int device);
+ /**
+ * @see AudioManager#setAllowedCapturePolicy()
+ */
+ public static native int setAllowedCapturePolicy(int uid, int flags);
+
static boolean isOffloadSupported(@NonNull AudioFormat format, @NonNull AudioAttributes attr) {
return native_is_offload_supported(format.getEncoding(), format.getSampleRate(),
format.getChannelMask(), format.getChannelIndexMask(),
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index e53b7e8..01a02f1 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -224,14 +224,4 @@
return new HwAudioSource(mAudioDeviceInfo, mAudioAttributes);
}
}
-
- /**
- * Eliminate {@link #deprecateStreamTypeForPlayback(int, String, String)} in API list.
- * TODO: remove this pseudo-override function
- * @hide
- */
- public static void deprecateStreamTypeForPlayback(int streamType, String className,
- String opName) throws IllegalArgumentException {
- // Do nothing.
- }
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 575a0bb..63b22df 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -18,6 +18,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -42,6 +43,8 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -355,6 +358,22 @@
public static final int HOTWORD = 1999;
}
+ /** @hide */
+ @IntDef({
+ AudioSource.DEFAULT,
+ AudioSource.MIC,
+ AudioSource.VOICE_UPLINK,
+ AudioSource.VOICE_DOWNLINK,
+ AudioSource.VOICE_CALL,
+ AudioSource.CAMCORDER,
+ AudioSource.VOICE_RECOGNITION,
+ AudioSource.VOICE_COMMUNICATION,
+ AudioSource.UNPROCESSED,
+ AudioSource.VOICE_PERFORMANCE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Source {}
+
// TODO make AudioSource static (API change) and move this method inside the AudioSource class
/**
* @hide
@@ -547,11 +566,11 @@
* to be specified before setting recording-parameters or encoders. Call
* this only before setOutputFormat().
*
- * @param audio_source the audio source to use
+ * @param audioSource the audio source to use
* @throws IllegalStateException if it is called after setOutputFormat()
* @see android.media.MediaRecorder.AudioSource
*/
- public native void setAudioSource(int audio_source)
+ public native void setAudioSource(@Source int audioSource)
throws IllegalStateException;
/**
diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp
new file mode 100644
index 0000000..f0fbc50
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/Android.bp
@@ -0,0 +1,14 @@
+android_test {
+ name: "mediaframeworktest",
+ srcs: ["**/*.java"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "mockito-target-minus-junit4",
+ "androidx.test.rules",
+ "android-ex-camera2",
+ ],
+ platform_apis: true,
+}
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
deleted file mode 100644
index 167d255..0000000
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- mockito-target-minus-junit4 \
- androidx.test.rules \
- android-ex-camera2
-
-LOCAL_PACKAGE_NAME := mediaframeworktest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
diff --git a/media/tests/MtpTests/Android.bp b/media/tests/MtpTests/Android.bp
new file mode 100644
index 0000000..7d2c7c6
--- /dev/null
+++ b/media/tests/MtpTests/Android.bp
@@ -0,0 +1,6 @@
+android_test {
+ name: "MtpTests",
+ srcs: ["**/*.java"],
+ static_libs: ["androidx.test.rules"],
+ platform_apis: true,
+}
diff --git a/media/tests/MtpTests/Android.mk b/media/tests/MtpTests/Android.mk
deleted file mode 100644
index 4cee62e..0000000
--- a/media/tests/MtpTests/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-
-LOCAL_PACKAGE_NAME := MtpTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
diff --git a/packages/Shell/tests/Android.mk b/packages/Shell/tests/Android.mk
index b93ddde..44ff338 100644
--- a/packages/Shell/tests/Android.mk
+++ b/packages/Shell/tests/Android.mk
@@ -9,7 +9,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
mockito-target-minus-junit4 \
ub-uiautomator \
junit \
diff --git a/packages/Shell/tests/AndroidManifest.xml b/packages/Shell/tests/AndroidManifest.xml
index 6d564c6..e845ef9 100644
--- a/packages/Shell/tests/AndroidManifest.xml
+++ b/packages/Shell/tests/AndroidManifest.xml
@@ -36,7 +36,7 @@
</activity>
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.shell"
android:label="Tests for Shell" />
diff --git a/packages/Shell/tests/AndroidTest.xml b/packages/Shell/tests/AndroidTest.xml
index e592d82..e03b68a 100644
--- a/packages/Shell/tests/AndroidTest.xml
+++ b/packages/Shell/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
<option name="test-tag" value="ShellTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.shell.tests" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java b/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java
index cef74ae..433eca2 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java
@@ -20,7 +20,6 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
@@ -29,12 +28,12 @@
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.test.mock.MockContext;
import android.util.Pair;
-import org.junit.Before;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index e69b0a8..3a71632 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -42,6 +42,40 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningServiceInfo;
+import android.app.Instrumentation;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.service.notification.StatusBarNotification;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener;
+
+import libcore.io.Streams;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
@@ -60,40 +94,6 @@
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
-import libcore.io.Streams;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestName;
-import org.junit.runner.RunWith;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningServiceInfo;
-import android.app.Instrumentation;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.Log;
-
-import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener;
-
/**
* Integration tests for {@link BugreportReceiver}.
* <p>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index a4592d5..3c6dc73 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -87,7 +87,8 @@
}
private void requestPulseOutNow(State dozeState) {
- if (dozeState == State.DOZE_REQUEST_PULSE || dozeState == State.DOZE_PULSING) {
+ if (dozeState == State.DOZE_REQUEST_PULSE || dozeState == State.DOZE_PULSING
+ || dozeState == State.DOZE_PULSING_BRIGHT) {
final int pulseReason = mMachine.getPulseReason();
if (pulseReason == DozeLog.PULSE_REASON_DOCKING) {
mDozeHost.stopPulsing();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 36e28dc..0607654 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -71,7 +71,7 @@
new DozeScreenState(wrappedService, handler, params, wakeLock),
createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
handler),
- new DozeWallpaperState(context, machine),
+ new DozeWallpaperState(context),
new DozeDockHandler(context, machine, host, config, handler, dockManager)
});
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index bd7a421..3c9d4a9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -33,7 +33,11 @@
boolean isProvisioned();
boolean isBlockingDoze();
- void extendPulse();
+ /**
+ * Makes a current pulse last for twice as long.
+ * @param reason why we're extending it.
+ */
+ void extendPulse(int reason);
void setAnimateWakeup(boolean animateWakeup);
void setAnimateScreenOff(boolean animateScreenOff);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index c243899..f65112c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -59,6 +59,8 @@
DOZE_REQUEST_PULSE,
/** Pulse is showing. Device is awake and showing UI. */
DOZE_PULSING,
+ /** Pulse is showing with bright wallpaper. Device is awake and showing UI. */
+ DOZE_PULSING_BRIGHT,
/** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */
DOZE_PULSE_DONE,
/** Doze is done. DozeService is finished. */
@@ -84,6 +86,7 @@
switch (this) {
case DOZE_REQUEST_PULSE:
case DOZE_PULSING:
+ case DOZE_PULSING_BRIGHT:
return true;
default:
return false;
@@ -101,6 +104,7 @@
case DOZE:
return Display.STATE_OFF;
case DOZE_PULSING:
+ case DOZE_PULSING_BRIGHT:
return Display.STATE_ON;
case DOZE_AOD:
case DOZE_AOD_PAUSING:
@@ -202,6 +206,7 @@
Assert.isMainThread();
Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE
|| mState == State.DOZE_PULSING
+ || mState == State.DOZE_PULSING_BRIGHT
|| mState == State.DOZE_PULSE_DONE, "must be in pulsing state, but is " + mState);
return mPulseReason;
}
@@ -283,7 +288,8 @@
break;
case DOZE_PULSE_DONE:
Preconditions.checkState(
- mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING);
+ mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING
+ || mState == State.DOZE_PULSING_BRIGHT);
break;
default:
break;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 7189a48..c9eb603 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -168,7 +168,7 @@
} else if (isPickup) {
gentleWakeUp(pulseReason);
} else {
- mDozeHost.extendPulse();
+ mDozeHost.extendPulse(pulseReason);
}
}, sensorPerformedProxCheck
|| (mDockManager != null && mDockManager.isDocked()), pulseReason);
@@ -212,7 +212,8 @@
final boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
final boolean aod = (state == DozeMachine.State.DOZE_AOD);
- if (state == DozeMachine.State.DOZE_PULSING) {
+ if (state == DozeMachine.State.DOZE_PULSING
+ || state == DozeMachine.State.DOZE_PULSING_BRIGHT) {
boolean ignoreTouch = near;
if (DEBUG) Log.i(TAG, "Prox changed, ignore touch = " + ignoreTouch);
mDozeHost.onIgnoreTouchWhilePulsing(ignoreTouch);
@@ -276,6 +277,7 @@
mDozeSensors.setListening(false);
break;
case DOZE_PULSING:
+ case DOZE_PULSING_BRIGHT:
mDozeSensors.setTouchscreenSensorsListening(false);
mDozeSensors.setProxListening(true);
break;
@@ -306,7 +308,16 @@
private void requestPulse(final int reason, boolean performedProxCheck) {
Assert.isMainThread();
- mDozeHost.extendPulse();
+ mDozeHost.extendPulse(reason);
+
+ // When already pulsing we're allowed to show the wallpaper directly without
+ // requesting a new pulse.
+ if (mMachine.getState() == DozeMachine.State.DOZE_PULSING
+ && reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ mMachine.requestState(DozeMachine.State.DOZE_PULSING_BRIGHT);
+ return;
+ }
+
if (mPulsePending || !mAllowPulseTriggers || !canPulse()) {
if (mAllowPulseTriggers) {
DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 847182d..51e96d2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -94,7 +94,10 @@
@Override
public void onPulseStarted() {
try {
- mMachine.requestState(DozeMachine.State.DOZE_PULSING);
+ mMachine.requestState(
+ reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
+ ? DozeMachine.State.DOZE_PULSING_BRIGHT
+ : DozeMachine.State.DOZE_PULSING);
} catch (IllegalStateException e) {
// It's possible that the pulse was asynchronously cancelled while
// we were waiting for it to start (under stress conditions.)
@@ -148,6 +151,7 @@
switch (state) {
case DOZE_REQUEST_PULSE:
case DOZE_PULSING:
+ case DOZE_PULSING_BRIGHT:
case DOZE_PULSE_DONE:
mHost.setAnimateWakeup(true);
break;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index ca9f24f..1b3cd88 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -38,19 +38,17 @@
private final IWallpaperManager mWallpaperManagerService;
private final DozeParameters mDozeParameters;
- private final DozeMachine mMachine;
private boolean mIsAmbientMode;
- public DozeWallpaperState(Context context, DozeMachine machine) {
- this(machine, IWallpaperManager.Stub.asInterface(
+ public DozeWallpaperState(Context context) {
+ this(IWallpaperManager.Stub.asInterface(
ServiceManager.getService(Context.WALLPAPER_SERVICE)),
DozeParameters.getInstance(context));
}
@VisibleForTesting
- DozeWallpaperState(DozeMachine machine, IWallpaperManager wallpaperManagerService,
+ DozeWallpaperState(IWallpaperManager wallpaperManagerService,
DozeParameters parameters) {
- mMachine = machine;
mWallpaperManagerService = wallpaperManagerService;
mDozeParameters = parameters;
}
@@ -65,16 +63,13 @@
case DOZE_AOD_PAUSED:
case DOZE_REQUEST_PULSE:
case DOZE_PULSE_DONE:
+ case DOZE_PULSING:
isAmbientMode = true;
break;
- case DOZE_PULSING:
- isAmbientMode =
- mMachine.getPulseReason() != DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
- break;
+ case DOZE_PULSING_BRIGHT:
default:
isAmbientMode = false;
}
-
final boolean animated;
if (isAmbientMode) {
animated = mDozeParameters.shouldControlScreenOff();
diff --git a/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt b/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt
index d7a2d9a..02ad0f1 100644
--- a/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt
@@ -17,7 +17,8 @@
val timeRemainingMillis: Long,
val severeThresholdMillis: Long,
val lowThresholdMillis: Long,
- val isBasedOnUsage: Boolean
+ val isBasedOnUsage: Boolean,
+ val isLowWarningEnabled: Boolean
) {
/**
* Returns whether hybrid warning logic/copy should be used for this snapshot
@@ -48,7 +49,8 @@
NO_ESTIMATE_AVAILABLE.toLong(),
NO_ESTIMATE_AVAILABLE.toLong(),
NO_ESTIMATE_AVAILABLE.toLong(),
- false
+ false,
+ true
) {
this.isHybrid = false
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java
index bd130f4..a879227 100644
--- a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java
+++ b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java
@@ -23,4 +23,9 @@
* show a severe warning to the user.
*/
long getSevereWarningThreshold();
+
+ /**
+ * Returns a boolean indicating if the low warning should be shown at all or not.
+ */
+ boolean getLowWarningEnabled();
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java
index 3f24176..bfb809e 100644
--- a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java
@@ -21,4 +21,9 @@
public long getSevereWarningThreshold() {
return 0;
}
+
+ @Override
+ public boolean getLowWarningEnabled() {
+ return true;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 1863860..4e41108 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -284,7 +284,8 @@
plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
mLowBatteryReminderLevels[0], estimate.getEstimateMillis(),
mEnhancedEstimates.getSevereWarningThreshold(),
- mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage());
+ mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage(),
+ mEnhancedEstimates.getLowWarningEnabled());
} else {
if (DEBUG) {
Slog.d(TAG, "using standard");
@@ -351,7 +352,6 @@
Slog.d(TAG, "Low warning marked as shown this cycle");
mLowWarningShownThisChargeCycle = true;
}
-
} else if (shouldDismissHybridWarning(currentSnapshot)) {
if (DEBUG) {
Slog.d(TAG, "Dismissing warning");
@@ -375,8 +375,9 @@
return false;
}
- // Only show the low warning once per charge cycle & no battery saver
- final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
+ // Only show the low warning if enabled once per charge cycle & no battery saver
+ final boolean canShowWarning = snapshot.isLowWarningEnabled()
+ && !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
&& (snapshot.getTimeRemainingMillis() < snapshot.getLowThresholdMillis()
|| snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold());
@@ -386,6 +387,7 @@
|| snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold());
final boolean canShow = canShowWarning || canShowSevereWarning;
+
if (DEBUG) {
Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith battery snapshot:"
+ " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 494e6cd..ead39c69 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -439,6 +439,9 @@
mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils
.supportsRoundedCornersOnWindows(mContext.getResources());
+ // Assumes device always starts with back button until launcher tells it that it does not
+ mBackButtonAlpha = 1.0f;
+
// Listen for the package update changes.
if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
updateEnabledState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1949bad..0d2fe13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -92,7 +92,7 @@
/**
* Scrim opacity when the phone is about to wake-up.
*/
- public static final float AOD2_SCRIM_ALPHA = 0.6f;
+ public static final float WAKE_SENSOR_SCRIM_ALPHA = 0.6f;
/**
* A scrim varies its opacity based on a busyness factor, for example
* how many notifications are currently visible.
@@ -458,6 +458,23 @@
mState.AOD.setAodFrontScrimAlpha(alpha);
}
+ /**
+ * If the lock screen sensor is active.
+ */
+ public void setWakeLockScreenSensorActive(boolean active) {
+ for (ScrimState state : ScrimState.values()) {
+ state.setWakeLockScreenSensorActive(active);
+ }
+
+ if (mState == ScrimState.PULSING) {
+ float newBehindAlpha = mState.getBehindAlpha();
+ if (mCurrentBehindAlpha != newBehindAlpha) {
+ mCurrentBehindAlpha = newBehindAlpha;
+ updateScrims();
+ }
+ }
+ }
+
protected void scheduleUpdate() {
if (mUpdatePending) return;
@@ -904,10 +921,6 @@
}
}
- public void setPulseReason(int pulseReason) {
- ScrimState.PULSING.setPulseReason(pulseReason);
- }
-
public interface Callback {
default void onStart() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 2f161d5..d152ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -19,7 +19,6 @@
import android.graphics.Color;
import android.os.Trace;
-import com.android.systemui.doze.DozeLog;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -129,16 +128,15 @@
@Override
public void prepare(ScrimState previousState) {
mCurrentInFrontAlpha = 0f;
- if (mPulseReason == DozeLog.PULSE_REASON_NOTIFICATION
- || mPulseReason == DozeLog.PULSE_REASON_DOCKING
- || mPulseReason == DozeLog.PULSE_REASON_INTENT) {
- mCurrentBehindAlpha = previousState.getBehindAlpha();
- } else {
- mCurrentBehindAlpha = ScrimController.AOD2_SCRIM_ALPHA;
- }
mCurrentBehindTint = Color.BLACK;
mBlankScreen = mDisplayRequiresBlanking;
}
+
+ @Override
+ public float getBehindAlpha() {
+ return mWakeLockScreenSensorActive ? ScrimController.WAKE_SENSOR_SCRIM_ALPHA
+ : AOD.getBehindAlpha();
+ }
},
/**
@@ -199,7 +197,7 @@
int mIndex;
boolean mHasBackdrop;
boolean mLaunchingAffordanceWithPreview;
- int mPulseReason;
+ boolean mWakeLockScreenSensorActive;
ScrimState(int index) {
mIndex = index;
@@ -264,10 +262,6 @@
mAodFrontScrimAlpha = aodFrontScrimAlpha;
}
- public void setPulseReason(int pulseReason) {
- mPulseReason = pulseReason;
- }
-
public void setScrimBehindAlphaKeyguard(float scrimBehindAlphaKeyguard) {
mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
}
@@ -287,4 +281,8 @@
public void setHasBackdrop(boolean hasBackdrop) {
mHasBackdrop = hasBackdrop;
}
+
+ public void setWakeLockScreenSensorActive(boolean active) {
+ mWakeLockScreenSensorActive = active;
+ }
}
\ No newline at end of file
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 d7f070f..db91d01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3908,7 +3908,6 @@
@Override
public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
- mScrimController.setPulseReason(reason);
if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:LONG_PRESS");
@@ -3916,6 +3915,10 @@
return;
}
+ if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ mScrimController.setWakeLockScreenSensorActive(true);
+ }
+
boolean passiveAuthInterrupt = reason == DozeLog.PULSE_REASON_NOTIFICATION;
// Set the state to pulsing, so ScrimController will know what to do once we ask it to
// execute the transition. The pulse callback will then be invoked when the scrims
@@ -3934,6 +3937,7 @@
mPulsing = false;
callback.onPulseFinished();
updateNotificationPanelTouchState();
+ mScrimController.setWakeLockScreenSensorActive(false);
setPulsing(false);
}
@@ -4010,7 +4014,10 @@
}
@Override
- public void extendPulse() {
+ public void extendPulse(int reason) {
+ if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ mScrimController.setWakeLockScreenSensorActive(true);
+ }
if (mDozeScrimController.isPulsing() && mAmbientPulseManager.hasNotifications()) {
mAmbientPulseManager.extendPulse();
} else {
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 cbaabf7..3b32d95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -760,9 +760,10 @@
}
public boolean bouncerNeedsScrimming() {
- return mOccluded || mBouncer.willDismissWithAction() || mBouncer.needsFullscreenBouncer()
+ return mOccluded || mBouncer.willDismissWithAction()
|| mStatusBar.isFullScreenUserSwitcherState()
- || (mBouncer.isShowing() && mBouncer.isScrimmed());
+ || (mBouncer.isShowing() && mBouncer.isScrimmed())
+ || mBouncer.isFullscreenBouncer();
}
public void dump(PrintWriter pw) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index dc42872..abfa755 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -86,7 +86,7 @@
}
@Override
- public void extendPulse() {
+ public void extendPulse(int reason) {
pulseExtended = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index beba905..87ae85f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -45,12 +45,11 @@
private DozeWallpaperState mDozeWallpaperState;
@Mock IWallpaperManager mIWallpaperManager;
@Mock DozeParameters mDozeParameters;
- @Mock DozeMachine mMachine;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDozeWallpaperState = new DozeWallpaperState(mMachine, mIWallpaperManager, mDozeParameters);
+ mDozeWallpaperState = new DozeWallpaperState(mIWallpaperManager, mDozeParameters);
}
@Test
@@ -110,28 +109,18 @@
}
@Test
- public void testTransitionTo_notificationPulseIsAmbientMode() throws RemoteException {
- when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_NOTIFICATION);
- mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
- DozeMachine.State.DOZE_PULSING);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
- }
-
- @Test
public void testTransitionTo_wakeFromPulseIsNotAmbientMode() throws RemoteException {
- when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN);
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD,
DozeMachine.State.DOZE_REQUEST_PULSE);
reset(mIWallpaperManager);
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
- DozeMachine.State.DOZE_PULSING);
+ DozeMachine.State.DOZE_PULSING_BRIGHT);
verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong());
}
@Test
public void testTransitionTo_animatesWhenWakingUpFromPulse() throws RemoteException {
- when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_NOTIFICATION);
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING);
reset(mIWallpaperManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index f51e473..5928a07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -265,6 +265,12 @@
state.mIsPowerSaver = true;
shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
assertThat(shouldShow).isFalse();
+
+ state.mIsPowerSaver = false;
+ // if disabled we should not show the low warning.
+ state.mIsLowLevelWarningEnabled = false;
+ shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+ assertThat(shouldShow).isFalse();
}
@Test
@@ -365,7 +371,7 @@
assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_HYBRID_THRESHOLD);
BatteryStateSnapshot snapshot = new BatteryStateSnapshot(
BATTERY_LEVEL_10, false, false, 0, BatteryManager.BATTERY_HEALTH_GOOD,
- 0, 0, -1, 0, 0, false);
+ 0, 0, -1, 0, 0, false, true);
mPowerUI.mLastBatteryStateSnapshot = snapshot;
// query again since the estimate was -1
@@ -375,7 +381,7 @@
assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_SEVERE_HYBRID_THRESHOLD);
snapshot = new BatteryStateSnapshot(
BATTERY_LEVEL_10, false, false, 0, BatteryManager.BATTERY_HEALTH_GOOD, 0,
- 0, BELOW_SEVERE_HYBRID_THRESHOLD, 0, 0, false);
+ 0, BELOW_SEVERE_HYBRID_THRESHOLD, 0, 0, false, true);
mPowerUI.mLastBatteryStateSnapshot = snapshot;
// Battery level hasn't changed, so we don't query again
@@ -536,13 +542,14 @@
public long mTimeRemainingMillis = Duration.ofHours(24).toMillis();
public boolean mIsBasedOnUsage = true;
public boolean mIsHybrid = true;
+ public boolean mIsLowLevelWarningEnabled = true;
public BatteryStateSnapshot get() {
if (mIsHybrid) {
return new BatteryStateSnapshot(mBatteryLevel, mIsPowerSaver, mPlugged, mBucket,
mBatteryStatus, mSevereLevelThreshold, mLowLevelThreshold,
mTimeRemainingMillis, mSevereThresholdMillis, mLowThresholdMillis,
- mIsBasedOnUsage);
+ mIsBasedOnUsage, mIsLowLevelWarningEnabled);
} else {
return new BatteryStateSnapshot(mBatteryLevel, mIsPowerSaver, mPlugged, mBucket,
mBatteryStatus, mSevereLevelThreshold, mLowLevelThreshold);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 539851f..191c983 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -46,7 +46,6 @@
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.util.function.TriConsumer;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.doze.DozeLog;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.utils.os.FakeHandler;
@@ -140,7 +139,6 @@
assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
// Pulsing notification should conserve AOD wallpaper.
- mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION);
mScrimController.transitionTo(ScrimState.PULSING);
mScrimController.finishAnimationsImmediately();
assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
@@ -225,14 +223,17 @@
mScrimController.finishAnimationsImmediately();
assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
- mScrimController.setPulseReason(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN);
mScrimController.transitionTo(ScrimState.PULSING);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be semi-transparent so the user can see the wallpaper
// Pulse callback should have been invoked
- assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
assertScrimTint(mScrimBehind, true /* tinted */);
+
+ mScrimController.setWakeLockScreenSensorActive(true);
+ mScrimController.finishAnimationsImmediately();
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
}
@Test
@@ -486,7 +487,6 @@
@Test
public void testHoldsPulsingWallpaperAnimationLock() {
// Pre-conditions
- mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION);
mScrimController.transitionTo(ScrimState.PULSING);
mScrimController.finishAnimationsImmediately();
reset(mWakeLock);
@@ -508,30 +508,6 @@
}
@Test
- public void testWillHidePulsingWallpaper_whenNotification() {
- mScrimController.setWallpaperSupportsAmbientMode(false);
- mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
- mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION);
- mScrimController.transitionTo(ScrimState.PULSING);
- mScrimController.finishAnimationsImmediately();
- assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
- assertScrimTint(mScrimBehind, true);
- }
-
- @Test
- public void testWillHidePulsingWallpaper_whenDocking() {
- mScrimController.setWallpaperSupportsAmbientMode(false);
- mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
- mScrimController.setPulseReason(DozeLog.PULSE_REASON_DOCKING);
- mScrimController.transitionTo(ScrimState.PULSING);
- mScrimController.finishAnimationsImmediately();
- assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
- assertScrimTint(mScrimBehind, true);
- }
-
- @Test
public void testConservesExpansionOpacityAfterTransition() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.setPanelExpansion(0.5f);
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 39f7f0f..6a9f5b6 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -357,10 +357,27 @@
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
}
+ private boolean shouldShutdownLocked() {
+ if (mHealthInfo.batteryLevel > 0) {
+ return false;
+ }
+
+ // Battery-less devices should not shutdown.
+ if (!mHealthInfo.batteryPresent) {
+ return false;
+ }
+
+ // If battery state is not CHARGING, shutdown.
+ // - If battery present and state == unknown, this is an unexpected error state.
+ // - If level <= 0 and state == full, this is also an unexpected state
+ // - All other states (NOT_CHARGING, DISCHARGING) means it is not charging.
+ return mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_CHARGING;
+ }
+
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
- if (mHealthInfo.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
+ if (shouldShutdownLocked()) {
mHandler.post(new Runnable() {
@Override
public void run() {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 8b10267..73e0439 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -627,11 +627,6 @@
r.callingPackage = callingPackage;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
- if (r.subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID && r.subId != subId) {
- throw new IllegalArgumentException(
- "PhoneStateListener cannot concurrently listen on multiple " +
- "subscriptions. Previously registered on subId: " + r.subId);
- }
// Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
// force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index b2c7ff3..87b272b 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -69,6 +69,9 @@
AudioEffect.Descriptor[] effects,
int activeSource, String packName) {
if (MediaRecorder.isSystemOnlyAudioSource(source)) {
+ // still want to log event, it just won't appear in recording configurations
+ sEventLogger.log(new RecordingEvent(event, uid, session, source, packName)
+ .printLog(TAG));
return;
}
String clientEffectName = clientEffects.length == 0 ? "None" : clientEffects[0].name;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 10f7db5..a2882de 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -961,7 +961,10 @@
@ServiceThreadOnly
void doManualPortSwitching(int portId, IHdmiControlCallback callback) {
assertRunOnServiceThread();
- // TODO: validate port ID
+ if (!mService.isValidPortId(portId)) {
+ invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
+ return;
+ }
if (portId == getLocalActivePort()) {
invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
return;
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
index 4359978..3e60ad4 100644
--- a/startop/iorap/tests/Android.bp
+++ b/startop/iorap/tests/Android.bp
@@ -16,32 +16,48 @@
java_library {
name: "libiorap-java-test-lib",
srcs: ["src/**/*.kt"],
-
static_libs: [
- // Non-test dependencies
-
- // library under test
- "services.startop.iorap",
- // need the system_server code to be on the classpath,
- "services.core",
-
- // Test Dependencies
-
- // test android dependencies
- "platform-test-annotations",
- "androidx.test.rules",
- // test framework dependencies
- "mockito-target-inline-minus-junit4",
- // "mockito-target-minus-junit4",
+ // Non-test dependencies
+ // library under test
+ "services.startop.iorap",
+ // need the system_server code to be on the classpath,
+ "services.core",
+ // Test Dependencies
+ // test android dependencies
+ "platform-test-annotations",
+ "androidx.test.rules",
+ // test framework dependencies
+ "mockito-target-inline-minus-junit4",
+ // "mockito-target-minus-junit4",
// Mockito also requires JNI (see Android.mk)
// and android:debuggable=true (see AndroidManifest.xml)
- "truth-prebuilt",
+ "truth-prebuilt",
],
-
// sdk_version: "current",
// certificate: "platform",
-
- libs: ["android.test.base", "android.test.runner"],
-
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
// test_suites: ["device-tests"],
}
+
+android_test {
+ name: "libiorap-java-tests",
+ dxflags: ["--multi-dex"],
+ test_suites: ["device-tests"],
+ static_libs: ["libiorap-java-test-lib"],
+ compile_multilib: "both",
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ "libmultiplejvmtiagentsinterferenceagent",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ // Use private APIs
+ certificate: "platform",
+ platform_apis: true,
+}
diff --git a/startop/iorap/tests/Android.mk b/startop/iorap/tests/Android.mk
deleted file mode 100644
index fa8c8b5..0000000
--- a/startop/iorap/tests/Android.mk
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright (C) 2018 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.
-
-# android_test does not support JNI libraries
-# TODO: once b/80095087 is fixed, rewrite this back to android_test
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PACKAGE_NAME := libiorap-java-tests
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- libiorap-java-test-lib
-
-LOCAL_MULTILIB := both
-
-LOCAL_JNI_SHARED_LIBRARIES := \
- libdexmakerjvmtiagent \
- libstaticjvmtiagent \
- libmultiplejvmtiagentsinterferenceagent
-
-LOCAL_JAVA_LIBRARIES := \
- android.test.base \
- android.test.runner
-
-# Use private APIs
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-# Disable presubmit test until it works with disabled iorap by default.
-LOCAL_PRESUBMIT_DISABLED := true
-
-include $(BUILD_PACKAGE)
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ffd5b16..e4debd6 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4845,22 +4845,18 @@
* Registers a listener object to receive notification of changes
* in specified telephony states.
* <p>
- * To register a listener, pass a {@link PhoneStateListener} and specify at least one telephony
- * state of interest in the events argument.
+ * To register a listener, pass a {@link PhoneStateListener}
+ * and specify at least one telephony state of interest in
+ * the events argument.
*
- * At registration, and when a specified telephony state changes, the telephony manager invokes
- * the appropriate callback method on the listener object and passes the current (updated)
- * values.
+ * At registration, and when a specified telephony state
+ * changes, the telephony manager invokes the appropriate
+ * callback method on the listener object and passes the
+ * current (updated) values.
* <p>
- * To un-register a listener, pass the listener object and set the events argument to
+ * To unregister a listener, pass the listener object and set the
+ * events argument to
* {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
- *
- * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
- * applies to the given subId. Otherwise, applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
- * pass a separate listener object to each TelephonyManager object created with
- * {@link #createForSubscriptionId}.
- *
* Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
* call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
* {@link SecurityException} will be thrown otherwise.
@@ -4875,18 +4871,17 @@
if (mContext == null) return;
try {
boolean notifyNow = (getITelephony() != null);
+ // If the listener has not explicitly set the subId (for example, created with the
+ // default constructor), replace the subId so it will listen to the account the
+ // telephony manager is created with.
+ if (listener.mSubId == null) {
+ listener.mSubId = mSubId;
+ }
+
ITelephonyRegistry registry = getTelephonyRegistry();
if (registry != null) {
- // listen to the subId the telephony manager is created with. Ignore subId in
- // PhoneStateListener.
- registry.listenForSubscriber(mSubId, getOpPackageName(),
+ registry.listenForSubscriber(listener.mSubId, getOpPackageName(),
listener.callback, events, notifyNow);
- // TODO: remove this once we remove PhoneStateListener constructor with subId.
- if (events == PhoneStateListener.LISTEN_NONE) {
- listener.mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- } else {
- listener.mSubId = mSubId;
- }
} else {
Rlog.w(TAG, "telephony registry not ready.");
}
@@ -10703,6 +10698,25 @@
}
/**
+ * It indicates whether modem is enabled or not per slot.
+ * It's the corresponding status of {@link #enableModemForSlot}.
+ *
+ * @param slotIndex which slot it's checking.
+ * @hide
+ */
+ public boolean isModemEnabledForSlot(int slotIndex) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isModemEnabledForSlot(slotIndex, mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "enableModem RemoteException", ex);
+ }
+ return false;
+ }
+
+ /**
* Broadcast intent action for network country code changes.
*
* <p>
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9e2d9ee..8332ffe 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1958,5 +1958,7 @@
/**
* Get the IRadio HAL Version encoded as 100 * MAJOR_VERSION + MINOR_VERSION or -1 if unknown
*/
- int getRadioHalVersion();
+ int getRadioHalVersion();
+
+ boolean isModemEnabledForSlot(int slotIndex, String callingPackage);
}
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index 41cb74a..e36f976 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -17,6 +17,7 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.server.wm.flicker"/>
+ <option name="exclude-annotation" value="org.junit.Ignore" />
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6000s" />
<option name="hidden-api-checks" value="false" />
diff --git a/keystore/tests/Android.mk b/tests/GamePerformance/Android.mk
similarity index 68%
rename from keystore/tests/Android.mk
rename to tests/GamePerformance/Android.mk
index 99d3197..58654de 100644
--- a/keystore/tests/Android.mk
+++ b/tests/GamePerformance/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright (C) 2018 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.
@@ -12,23 +12,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
+
include $(CLEAR_VARS)
-# LOCAL_MODULE := keystore
+# Don't include this package in any target
LOCAL_MODULE_TAGS := tests
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.rules hamcrest-library
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-LOCAL_PACKAGE_NAME := KeystoreTests
+LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner
+
+LOCAL_PACKAGE_NAME := GamePerformance
+
LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
LOCAL_CERTIFICATE := platform
-include $(BUILD_PACKAGE)
+include $(BUILD_PACKAGE)
diff --git a/tests/GamePerformance/AndroidManifest.xml b/tests/GamePerformance/AndroidManifest.xml
new file mode 100644
index 0000000..b331e2c
--- /dev/null
+++ b/tests/GamePerformance/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.gameperformance">
+ <uses-sdk android:minSdkVersion="25"/>
+ <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application android:theme="@style/noeffects">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.gameperformance.GamePerformanceActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="android.gameperformance">
+ </instrumentation>
+</manifest>
diff --git a/tests/GamePerformance/res/values/themes.xml b/tests/GamePerformance/res/values/themes.xml
new file mode 100644
index 0000000..6313071
--- /dev/null
+++ b/tests/GamePerformance/res/values/themes.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+<resources>
+ <style name="noeffects" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:fadingEdge">none</item>
+ <item name="android:windowContentTransitions">false</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ </style>
+</resources>
diff --git a/tests/GamePerformance/src/android/gameperformance/ATraceRunner.java b/tests/GamePerformance/src/android/gameperformance/ATraceRunner.java
new file mode 100644
index 0000000..25754fd
--- /dev/null
+++ b/tests/GamePerformance/src/android/gameperformance/ATraceRunner.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 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.gameperformance;
+
+import java.io.BufferedReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import android.app.Instrumentation;
+import android.os.AsyncTask;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+/**
+ * Helper that runs atrace command for required duration and category. Results are read from
+ * the output of atrace and serialized to the provided file. We cannot use direct atrace to
+ * file because atrace is executed in UI automator context and analysis is done in test context.
+ * In last case output file is not accessible from the both contexts.
+ */
+public class ATraceRunner extends AsyncTask<Void, Integer, Boolean>{
+ private final static String TAG = "ATraceRunner";
+
+ // Report that atrace is done.
+ public interface Delegate {
+ public void onProcessed(boolean success);
+ }
+
+ private final Instrumentation mInstrumentation;
+ private final String mOutput;
+ private final int mTimeInSeconds;
+ private final String mCategory;
+ private final Delegate mDelegate;
+
+ public ATraceRunner(Instrumentation instrumentation,
+ String output,
+ int timeInSeconds,
+ String category,
+ Delegate delegate) {
+ mInstrumentation = instrumentation;
+ mOutput = output;
+ mTimeInSeconds = timeInSeconds;
+ mCategory = category;
+ mDelegate = delegate;
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ BufferedReader bufferedReader = null;
+ FileWriter writer = null;
+ try {
+ // Run the command.
+ final String cmd = "atrace -t " + mTimeInSeconds + " " + mCategory;
+ Log.i(TAG, "Running atrace... " + cmd);
+ writer = new FileWriter(mOutput);
+ final ParcelFileDescriptor fd =
+ mInstrumentation.getUiAutomation().executeShellCommand(cmd);
+ bufferedReader = new BufferedReader(
+ new InputStreamReader(new ParcelFileDescriptor.AutoCloseInputStream(fd)));
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ writer.write(line);
+ writer.write("\n");
+ }
+ Log.i(TAG, "Running atrace... DONE");
+ return true;
+ } catch (IOException e) {
+ Log.i(TAG, "atrace failed", e);
+ return false;
+ } finally {
+ Utils.closeQuietly(bufferedReader);
+ Utils.closeQuietly(writer);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ mDelegate.onProcessed(result);
+ }
+
+}
diff --git a/tests/GamePerformance/src/android/gameperformance/CustomOpenGLView.java b/tests/GamePerformance/src/android/gameperformance/CustomOpenGLView.java
new file mode 100644
index 0000000..2b37280
--- /dev/null
+++ b/tests/GamePerformance/src/android/gameperformance/CustomOpenGLView.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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.gameperformance;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+
+public class CustomOpenGLView extends GLSurfaceView {
+ private Random mRandom;
+ private List<Long> mFrameTimes;
+
+ public CustomOpenGLView(Context context) {
+ super(context);
+
+ mRandom = new Random();
+ mFrameTimes = new ArrayList<Long>();
+
+ setEGLContextClientVersion(2);
+
+ setRenderer(new GLSurfaceView.Renderer() {
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
+ gl.glClearDepthf(1.0f);
+ gl.glEnable(GL10.GL_DEPTH_TEST);
+ gl.glDepthFunc(GL10.GL_LEQUAL);
+
+ gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
+ GL10.GL_NICEST); }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ GLES20.glViewport(0, 0, width, height);
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ GLES20.glClearColor(
+ mRandom.nextFloat(), mRandom.nextFloat(), mRandom.nextFloat(), 1.0f);
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ synchronized (mFrameTimes) {
+ mFrameTimes.add(System.currentTimeMillis());
+ }
+ }
+ });
+ setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
+ }
+
+ /**
+ * Resets frame times in order to calculate fps for different test pass.
+ */
+ public void resetFrameTimes() {
+ synchronized (mFrameTimes) {
+ mFrameTimes.clear();
+ }
+ }
+
+ /**
+ * Returns current fps based on collected frame times.
+ */
+ public double getFps() {
+ synchronized (mFrameTimes) {
+ if (mFrameTimes.size() < 2) {
+ return 0.0f;
+ }
+ return 1000.0 * mFrameTimes.size() /
+ (mFrameTimes.get(mFrameTimes.size() - 1) - mFrameTimes.get(0));
+ }
+ }
+}
diff --git a/tests/GamePerformance/src/android/gameperformance/CustomSurfaceView.java b/tests/GamePerformance/src/android/gameperformance/CustomSurfaceView.java
new file mode 100644
index 0000000..5616136
--- /dev/null
+++ b/tests/GamePerformance/src/android/gameperformance/CustomSurfaceView.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2018 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.gameperformance;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Trace;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * Minimal SurfaceView that sends buffer on request.
+ */
+public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+ // Tag for trace when buffer is requested.
+ public final static String LOCAL_REQUEST_BUFFER = "localRequestBuffer";
+ // Tag for trace when buffer is posted.
+ public final static String LOCAL_POST_BUFFER = "localPostBuffer";
+
+ private final Object mSurfaceLock = new Object();
+ // Keeps frame times. Used to calculate fps.
+ private List<Long> mFrameTimes;
+ // Surface to send.
+ private Surface mSurface;
+ private Handler mHandler;
+
+ private Runnable mInvalidateSurfaceTask = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mSurfaceLock) {
+ if (mSurface == null) {
+ return;
+ }
+ invalidateSurface(true, true);
+ mHandler.post(this);
+ }
+ }
+ };
+
+ public CustomSurfaceView(Context context) {
+ super(context);
+ mFrameTimes = new ArrayList<Long>();
+ getHolder().addCallback(this);
+ getHolder().setFormat(PixelFormat.OPAQUE);
+
+ HandlerThread thread = new HandlerThread("SurfaceInvalidator");
+ thread.start();
+ mHandler = new Handler(thread.getLooper());
+ }
+
+ /**
+ * Resets frame times in order to calculate fps for different test pass.
+ */
+ public void resetFrameTimes() {
+ synchronized (mSurfaceLock) {
+ mFrameTimes.clear();
+ }
+ }
+
+ /**
+ * Returns current fps based on collected frame times.
+ */
+ public double getFps() {
+ synchronized (mSurfaceLock) {
+ if (mFrameTimes.size() < 2) {
+ return 0.0f;
+ }
+ return 1000.0 * mFrameTimes.size() /
+ (mFrameTimes.get(mFrameTimes.size() - 1) - mFrameTimes.get(0));
+ }
+ }
+
+ /**
+ * Invalidates surface.
+ * @param traceCalls set to true in case we need register trace calls. Not used for warm-up.
+ * @param drawFps perform drawing current fps on surface to have some payload on surface.
+ */
+ public void invalidateSurface(boolean traceCalls, boolean drawFps) {
+ synchronized (mSurfaceLock) {
+ if (mSurface == null) {
+ throw new IllegalStateException("Surface is not ready");
+ }
+ if (traceCalls) {
+ Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, LOCAL_REQUEST_BUFFER);
+ }
+ Canvas canvas = mSurface.lockCanvas(null);
+ if (traceCalls) {
+ Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+ }
+
+ if (drawFps) {
+ int textSize = canvas.getHeight() / 24;
+ Paint paint = new Paint();
+ paint.setTextSize(textSize);
+ paint.setColor(0xFFFF8040);
+ canvas.drawARGB(92, 255, 255, 255);
+ canvas.drawText("FPS: " + String.format("%.2f", getFps()), 10, 300, paint);
+ }
+
+ if (traceCalls) {
+ Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, LOCAL_POST_BUFFER);
+ }
+ mSurface.unlockCanvasAndPost(canvas);
+ if (traceCalls) {
+ Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+ }
+
+ mFrameTimes.add(System.currentTimeMillis());
+ }
+ }
+
+ /**
+ * Wait until surface is created and ready to use or return immediately if surface
+ * already exists.
+ */
+ public void waitForSurfaceReady() {
+ synchronized (mSurfaceLock) {
+ if (mSurface == null) {
+ try {
+ mSurfaceLock.wait(5000);
+ } catch(InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (mSurface == null)
+ throw new IllegalStateException("Surface is not ready.");
+ }
+ }
+
+ /**
+ * Waits until surface is destroyed or return immediately if surface does not exist.
+ */
+ public void waitForSurfaceDestroyed() {
+ synchronized (mSurfaceLock) {
+ if (mSurface != null) {
+ try {
+ mSurfaceLock.wait(5000);
+ } catch(InterruptedException e) {
+ }
+ }
+ if (mSurface != null)
+ throw new IllegalStateException("Surface still exists.");
+ }
+ }
+
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ // This method is always called at least once, after surfaceCreated.
+ synchronized (mSurfaceLock) {
+ mSurface = holder.getSurface();
+ mSurfaceLock.notify();
+ mHandler.post(mInvalidateSurfaceTask);
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ synchronized (mSurfaceLock) {
+ mHandler.removeCallbacks(mInvalidateSurfaceTask);
+ mSurface = null;
+ mSurfaceLock.notify();
+ }
+ }
+}
diff --git a/tests/GamePerformance/src/android/gameperformance/GamePerformanceActivity.java b/tests/GamePerformance/src/android/gameperformance/GamePerformanceActivity.java
new file mode 100644
index 0000000..b0e6196
--- /dev/null
+++ b/tests/GamePerformance/src/android/gameperformance/GamePerformanceActivity.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 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.gameperformance;
+
+import java.util.concurrent.CountDownLatch;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.RelativeLayout;
+
+/**
+ * Minimal activity that holds SurfaceView or GLSurfaceView.
+ * call attachSurfaceView or attachOpenGLView to switch views.
+ */
+public class GamePerformanceActivity extends Activity {
+ private CustomSurfaceView mSurfaceView = null;
+ private CustomOpenGLView mOpenGLView = null;
+ private RelativeLayout mRootLayout;
+
+ public void attachSurfaceView() throws InterruptedException {
+ synchronized (mRootLayout) {
+ if (mSurfaceView != null) {
+ return;
+ }
+ final CountDownLatch latch = new CountDownLatch(1);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (mOpenGLView != null) {
+ mRootLayout.removeView(mOpenGLView);
+ mOpenGLView = null;
+ }
+ mSurfaceView = new CustomSurfaceView(GamePerformanceActivity.this);
+ mRootLayout.addView(mSurfaceView);
+ latch.countDown();
+ }
+ });
+ latch.await();
+ mSurfaceView.waitForSurfaceReady();
+ }
+ }
+
+ public void attachOpenGLView() throws InterruptedException {
+ synchronized (mRootLayout) {
+ if (mOpenGLView != null) {
+ return;
+ }
+ final CountDownLatch latch = new CountDownLatch(1);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (mSurfaceView != null) {
+ mRootLayout.removeView(mSurfaceView);
+ mSurfaceView = null;
+ }
+ mOpenGLView = new CustomOpenGLView(GamePerformanceActivity.this);
+ mRootLayout.addView(mOpenGLView);
+ latch.countDown();
+ }
+ });
+ latch.await();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ // To layouts in parent. First contains list of Surfaces and second
+ // controls. Controls stay on top.
+ mRootLayout = new RelativeLayout(this);
+ mRootLayout.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ Rect rect = new Rect();
+ getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
+
+ mOpenGLView = new CustomOpenGLView(this);
+ mRootLayout.addView(mOpenGLView);
+
+ setContentView(mRootLayout);
+ }
+
+ public void resetFrameTimes() {
+ if (mSurfaceView != null) {
+ mSurfaceView.resetFrameTimes();
+ } else if (mOpenGLView != null) {
+ mOpenGLView.resetFrameTimes();
+ } else {
+ throw new IllegalStateException("Nothing attached");
+ }
+ }
+
+ public double getFps() {
+ if (mSurfaceView != null) {
+ return mSurfaceView.getFps();
+ } else if (mOpenGLView != null) {
+ return mOpenGLView.getFps();
+ } else {
+ throw new IllegalStateException("Nothing attached");
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+}
\ No newline at end of file
diff --git a/tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java b/tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java
new file mode 100644
index 0000000..e5de7d7
--- /dev/null
+++ b/tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 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.gameperformance;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Trace;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+public class GamePerformanceTest extends
+ ActivityInstrumentationTestCase2<GamePerformanceActivity> {
+ private final static String TAG = "GamePerformanceTest";
+
+ private final static int GRAPHIC_BUFFER_WARMUP_LOOP_CNT = 60;
+
+ public GamePerformanceTest() {
+ super(GamePerformanceActivity.class);
+ }
+
+ @SmallTest
+ public void testGraphicBufferMetrics() throws IOException, InterruptedException {
+ Bundle status = new Bundle();
+
+ for (int i = 0; i < 2; ++i) {
+ if (i == 0) {
+ getActivity().attachSurfaceView();
+ } else {
+ getActivity().attachOpenGLView();
+ }
+
+ // Perform warm-up.
+ Thread.sleep(2000);
+
+ // Once atrace is done, this one is triggered.
+ CountDownLatch latch = new CountDownLatch(1);
+
+ final String passTag = i == 0 ? "surface" : "opengl";
+ final String output = (new File(getInstrumentation().getContext().getFilesDir(),
+ "atrace_" + passTag + ".log")).getAbsolutePath();
+ Log.i(TAG, "Collecting traces to " + output);
+ new ATraceRunner(getInstrumentation(), output, 5, "gfx", new ATraceRunner.Delegate() {
+ @Override
+ public void onProcessed(boolean success) {
+ latch.countDown();
+ }
+ }).execute();
+
+ // Reset frame times and perform invalidation loop while atrace is running.
+ getActivity().resetFrameTimes();
+ latch.await();
+
+ // Copy results.
+ final Map<String, Double> metrics =
+ GraphicBufferMetrics.processGraphicBufferResult(output, passTag);
+ for (Map.Entry<String, Double> metric : metrics.entrySet()) {
+ status.putDouble(metric.getKey(), metric.getValue());
+ }
+ // Also record FPS.
+ status.putDouble(passTag + "_fps", getActivity().getFps());
+ }
+
+ getInstrumentation().sendStatus(Activity.RESULT_OK, status);
+ }
+}
diff --git a/tests/GamePerformance/src/android/gameperformance/GraphicBufferMetrics.java b/tests/GamePerformance/src/android/gameperformance/GraphicBufferMetrics.java
new file mode 100644
index 0000000..dffce1a
--- /dev/null
+++ b/tests/GamePerformance/src/android/gameperformance/GraphicBufferMetrics.java
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2018 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.gameperformance;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * Utility class that performs analysis of atrace logs. This is implemented without Android
+ * dependencies and therefore can be used in stand-alone mode.
+ * Idea of this is to track atrace gfx event from graphics buffer producer/consumer.
+ * We analyze here from surfaceflinger
+ * queueBuffer - event when buffer was queued.
+ * acquireBuffer - event when buffer was requested for composition.
+ * releaseBuffer - even when buffer was released after composition.
+ * This also track events, issued locally
+ * localPostBuffer - event when buffer was posted from the local app.
+ *
+ * queueBuffer, acquireBuffer, releaseBuffer is accompanied with buffer name so we
+ * can track life-cycle of particular buffer.
+ * We don't have such information for localPostBuffer, however we can track next queueBuffer
+ * from surfaceflinger corresponds to previous localPostBuffer.
+ *
+ * Following results are calculated:
+ * post_time_[min/max/avr]_mcs - time for localPostBuffer duration.
+ * ready_time_[min/max/avr]_mcs - time from localPostBuffer to when buffer was acquired by
+ * surfaceflinger.
+ * latency_[min/max/avr]_mcs - time from localPostBuffer to when buffer was released by
+ * surfaceflinger.
+ * missed_frame_percents - percentage of missed frames (frames that do not have right sequence
+ * of events).
+ *
+ * Following is example of atrace logs from different platforms
+ * <...>-5237 (-----) [000] ...1 228.380392: tracing_mark_write: B|11|SurfaceView - android.gameperformance/android.gameperformance.GamePerformanceActivity#0: 2
+ * surfaceflinger-5855 ( 5855) [001] ...1 169.627364: tracing_mark_write: B|24|acquireBuffer
+ * HwBinder:617_2-652 ( 617) [002] d..1 360262.921756: sde_evtlog: 617|sde_encoder_virt_atomic_check:855|19|0|0|0|0|0|0|0|0|0|0|0|0|0|0
+ */
+public class GraphicBufferMetrics {
+ private final static String TAG = "GraphicBufferMetrics";
+
+ private final static String KEY_POST_TIME = "post_time";
+ private final static String KEY_READY_TIME = "ready_time";
+ private final static String KEY_LATENCY = "latency";
+ private final static String SUFFIX_MIN = "min";
+ private final static String SUFFIX_MAX = "max";
+ private final static String SUFFIX_MEDIAN = "median";
+ private final static String KEY_MISSED_FRAME_RATE = "missed_frame_percents";
+
+ private final static int EVENT_POST_BUFFER = 0;
+ private final static int EVENT_QUEUE_BUFFER = 1;
+ private final static int EVENT_ACQUIRE_BUFFER = 2;
+ private final static int EVENT_RELEASE_BUFFER = 3;
+
+ // atrace prints this line. Used as a marker to make sure that we can parse its output.
+ private final static String ATRACE_HEADER =
+ "# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION";
+
+ private final static String[] KNOWN_PHRASES = new String[] {
+ "capturing trace... done", "TRACE:"};
+ private final static List<String> KNWON_PHRASES_LIST = Arrays.asList(KNOWN_PHRASES);
+
+ // Type of the atrace event we can parse and analyze.
+ private final static String FUNCTION_TRACING_MARK_WRITE = "tracing_mark_write";
+
+ // Trace event we can ignore. It contains current timestamp information for the atrace output.
+ private final static String TRACE_EVENT_CLOCK_SYNC = "trace_event_clock_sync:";
+
+ // Threshold we consider test passes successfully. If we cannot collect enough amount of frames
+ // let fail the test. 50 is calculated 10 frames per second running for five seconds.
+ private final static int MINIMAL_SAMPLE_CNT_TO_PASS = 50;
+
+ /**
+ * Raw event in atrace. Stored hierarchically.
+ */
+ private static class RawEvent {
+ // Parent of this event or null for the root holder.
+ public final RawEvent mParent;
+ // Time of the event in mcs.
+ public final long mTime;
+ // Duration of the event in mcs.
+ public long mDuration;
+ // Name/body of the event.
+ public final String mName;
+ // Children events.
+ public final List<RawEvent> mChildren;
+
+ public RawEvent(RawEvent parent, long time, String name) {
+ mParent = parent;
+ mTime = time;
+ mName = name;
+ mDuration = -1;
+ mChildren = new ArrayList<>();
+ }
+
+ /**
+ * Recursively finds child events.
+ * @param path specify path to events. For example a/b. That means to find child with name
+ * 'a' of the current event and in this child find child with name 'b'. Path
+ * can consist from only one segment and that means we analyze only children of
+ * the current event.
+ * @param collector to collect found events.
+ */
+ public void findEvents(String path, List<RawEvent> collector) {
+ final int separator = path.indexOf('/');
+ final String current = separator > 0 ? path.substring(0, separator) : path;
+ final String nextPath = separator > 0 ? path.substring(separator + 1) : null;
+ for (RawEvent child : mChildren) {
+ if (child.mName.equals(current)) {
+ if (nextPath != null) {
+ child.findEvents(nextPath, collector);
+ } else {
+ collector.add(child);
+ }
+ }
+ }
+ }
+
+ public void dump(String prefix) {
+ System.err.print(prefix);
+ System.err.println(mTime + "[" + mDuration + "] " + mName);
+ for (RawEvent e : mChildren) {
+ e.dump(prefix + " ");
+ }
+ }
+ }
+
+ /**
+ * Describes graphic buffer event. local post, queued, acquired, released.
+ */
+ private static class BufferEvent {
+ public final int mType;
+ public final long mTime;
+ public final long mDuration;
+ public final String mBufferId;
+
+ public BufferEvent(int type, long time, long duration, String bufferId) {
+ mType = type;
+ mTime = time;
+ mDuration = duration;
+ mBufferId = bufferId;
+ }
+
+ @Override
+ public String toString() {
+ return "Type: " + mType + ". Time: " + mTime +
+ "[" + mDuration + "]. Buffer: " + mBufferId + ".";
+ }
+ }
+
+ /**
+ * Returns true if given char is digit.
+ */
+ private static boolean isDigitChar(char c) {
+ return (c >= '0') && (c <= '9');
+ }
+
+ /**
+ * Returns true if given char is digit or '.'.
+ */
+ private static boolean isDoubleDigitChar(char c) {
+ return (c == '.') || isDigitChar(c);
+ }
+
+ /**
+ * Convert timestamp string that represents double value in seconds to long time that represents
+ * timestamp in microseconds.
+ */
+ private static long getTimeStamp(String timeStampStr) {
+ return (long)(1000000.0 * Double.parseDouble(timeStampStr));
+ }
+
+ /**
+ * Reads atrace log and build event model. Result is a map, where key specifies event for the
+ * particular thread. Value is the synthetic root RawEvent that holds all events for the
+ * thread. Events are stored hierarchically.
+ */
+ private static Map<Integer, RawEvent> buildEventModel(String fileName) throws IOException {
+ Map<Integer, RawEvent> result = new HashMap<>();
+
+ BufferedReader bufferedReader = null;
+ String line = "";
+ boolean headerDetected = false;
+ try {
+ bufferedReader = new BufferedReader(new FileReader(fileName));
+ while ((line = bufferedReader.readLine()) != null) {
+ // Make sure we find comment that describes output format we can with.
+ headerDetected |= line.equals(ATRACE_HEADER);
+ // Skip comments.
+ if (line.startsWith("#")) {
+ continue;
+ }
+ // Skip known service output
+ if (KNWON_PHRASES_LIST.contains(line)) {
+ continue;
+ }
+
+ if (!headerDetected) {
+ // We don't know the format of this line.
+ throw new IllegalStateException("Header was not detected");
+ }
+
+ // TASK-PID in header exists at position 12. PID position 17 should contains first
+ // digit of thread id after the '-'.
+ if (!isDigitChar(line.charAt(17)) || line.charAt(16) != '-') {
+ throw new IllegalStateException("Failed to parse thread id: " + line);
+ }
+ int rightIndex = line.indexOf(' ', 17);
+ final String threadIdStr = line.substring(17, rightIndex);
+ final int threadId = Integer.parseInt(threadIdStr);
+
+ // TIMESTAMP in header exists at position 45
+ // This position should point in the middle of timestamp which is ended by ':'.
+ int leftIndex = 45;
+ while (isDoubleDigitChar(line.charAt(leftIndex))) {
+ --leftIndex;
+ }
+ rightIndex = line.indexOf(':', 45);
+
+ final String timeStampString = line.substring(leftIndex + 1, rightIndex);
+ final long timeStampMcs = getTimeStamp(timeStampString);
+
+ // Find function name, pointed by FUNCTION. Long timestamp can shift if position
+ // so use end of timestamp to find the function which is ended by ':'.
+ leftIndex = rightIndex + 1;
+ while (Character.isWhitespace(line.charAt(leftIndex))) {
+ ++leftIndex;
+ }
+ rightIndex = line.indexOf(':', leftIndex);
+ final String function = line.substring(leftIndex, rightIndex);
+
+ if (!function.equals(FUNCTION_TRACING_MARK_WRITE)) {
+ continue;
+ }
+
+ // Rest of the line is event body.
+ leftIndex = rightIndex + 1;
+ while (Character.isWhitespace(line.charAt(leftIndex))) {
+ ++leftIndex;
+ }
+
+ final String event = line.substring(leftIndex);
+ if (event.startsWith(TRACE_EVENT_CLOCK_SYNC)) {
+ continue;
+ }
+
+ // Parse event, for example:
+ // B|615|SurfaceView - android.gameperformance.GamePerformanceActivity#0: 1
+ // E|615
+ // C|11253|operation id|2
+ StringTokenizer eventTokenizer = new StringTokenizer(event, "|");
+ final String eventType = eventTokenizer.nextToken();
+
+ // Attach root on demand.
+ if (!result.containsKey(threadId)) {
+ result.put(threadId, new RawEvent(null /* parent */,
+ timeStampMcs,
+ "#ROOT_" + threadId));
+ }
+
+ switch (eventType) {
+ case "B": {
+ // Log entry starts.
+ eventTokenizer.nextToken(); // PID
+ String eventText = eventTokenizer.nextToken();
+ while (eventTokenizer.hasMoreTokens()) {
+ eventText += " ";
+ eventText += eventTokenizer.nextToken();
+ }
+ RawEvent parent = result.get(threadId);
+ RawEvent current = new RawEvent(parent, timeStampMcs, eventText);
+ parent.mChildren.add(current);
+ result.put(threadId, current);
+ }
+ break;
+ case "E": {
+ // Log entry ends.
+ RawEvent current = result.get(threadId);
+ current.mDuration = timeStampMcs - current.mTime;
+ if (current.mParent == null) {
+ // Detect a tail of the previous call. Remove last child element if it
+ // exists once it does not belong to the root.
+ if (!current.mChildren.isEmpty()) {
+ current.mChildren.remove(current.mChildren.size() -1);
+ }
+ } else {
+ result.put(threadId, current.mParent);
+ }
+ }
+ break;
+ case "C":
+ // Counter, ignore
+ break;
+ default:
+ throw new IllegalStateException(
+ "Unrecognized trace: " + line + " # " + eventType + " # " + event);
+ }
+ }
+
+ // Detect incomplete events and detach from the root.
+ Set<Integer> threadIds = new TreeSet<>();
+ threadIds.addAll(result.keySet());
+ for (int threadId : threadIds) {
+ RawEvent root = result.get(threadId);
+ if (root.mParent == null) {
+ // Last trace was closed.
+ continue;
+ }
+ // Find the root.
+ while (root.mParent != null) {
+ root = root.mParent;
+ }
+ // Discard latest incomplete element.
+ root.mChildren.remove(root.mChildren.size() - 1);
+ result.put(threadId, root);
+ }
+ } catch (Exception e) {
+ throw new IOException("Failed to process " + line, e);
+ } finally {
+ Utils.closeQuietly(bufferedReader);
+ }
+
+ return result;
+ }
+
+ /**
+ * Processes provided atrace log and calculates graphics buffer metrics.
+ * @param fileName name of atrace log file.
+ * @param testTag tag to separate results for the different passes.
+ */
+ public static Map<String, Double> processGraphicBufferResult(
+ String fileName, String testTag) throws IOException {
+ final Map<Integer, RawEvent> model = buildEventModel(fileName);
+
+ List<RawEvent> collectorPostBuffer = new ArrayList<>();
+ List<RawEvent> collectorQueueBuffer = new ArrayList<>();
+ List<RawEvent> collectorReleaseBuffer = new ArrayList<>();
+ List<RawEvent> collectorAcquireBuffer = new ArrayList<>();
+
+ // Collect required events.
+ for (RawEvent root : model.values()) {
+ // Surface view
+ root.findEvents("localPostBuffer", collectorPostBuffer);
+ // OpengGL view
+ root.findEvents("eglSwapBuffersWithDamageKHR", collectorPostBuffer);
+
+ root.findEvents("queueBuffer", collectorQueueBuffer);
+ root.findEvents("onMessageReceived/handleMessageInvalidate/latchBuffer/" +
+ "updateTexImage/acquireBuffer",
+ collectorAcquireBuffer);
+ // PI stack
+ root.findEvents(
+ "onMessageReceived/handleMessageRefresh/postComposition/releaseBuffer",
+ collectorReleaseBuffer);
+ // NYC stack
+ root.findEvents(
+ "onMessageReceived/handleMessageRefresh/releaseBuffer",
+ collectorReleaseBuffer);
+ }
+
+ // Convert raw event to buffer events.
+ List<BufferEvent> bufferEvents = new ArrayList<>();
+ for (RawEvent event : collectorPostBuffer) {
+ bufferEvents.add(
+ new BufferEvent(EVENT_POST_BUFFER, event.mTime, event.mDuration, null));
+ }
+ toBufferEvents(EVENT_QUEUE_BUFFER, collectorQueueBuffer, bufferEvents);
+ toBufferEvents(EVENT_ACQUIRE_BUFFER, collectorAcquireBuffer, bufferEvents);
+ toBufferEvents(EVENT_RELEASE_BUFFER, collectorReleaseBuffer, bufferEvents);
+
+ // Sort events based on time. These events are originally taken from different threads so
+ // order is not always preserved.
+ Collections.sort(bufferEvents, new Comparator<BufferEvent>() {
+ @Override
+ public int compare(BufferEvent o1, BufferEvent o2) {
+ if (o1.mTime < o2.mTime) {
+ return -1;
+ } if (o1.mTime > o2.mTime) {
+ return +1;
+ } else {
+ return 0;
+ }
+ }
+ });
+
+ // Collect samples.
+ List<Long> postTimes = new ArrayList<>();
+ List<Long> readyTimes = new ArrayList<>();
+ List<Long> latencyTimes = new ArrayList<>();
+ long missedCnt = 0;
+
+ for (int i = 0; i < bufferEvents.size(); ++i) {
+ if (bufferEvents.get(i).mType != EVENT_POST_BUFFER) {
+ continue;
+ }
+ final int queueIndex = findNextOfType(bufferEvents, i + 1, EVENT_QUEUE_BUFFER);
+ if (queueIndex < 0) {
+ break;
+ }
+ final int acquireIndex = findNextOfBufferId(bufferEvents, queueIndex + 1,
+ bufferEvents.get(queueIndex).mBufferId);
+ if (acquireIndex < 0) {
+ break;
+ }
+ if (bufferEvents.get(acquireIndex).mType != EVENT_ACQUIRE_BUFFER) {
+ // Was not actually presented.
+ ++missedCnt;
+ continue;
+ }
+ final int releaseIndex = findNextOfBufferId(bufferEvents, acquireIndex + 1,
+ bufferEvents.get(queueIndex).mBufferId);
+ if (releaseIndex < 0) {
+ break;
+ }
+ if (bufferEvents.get(releaseIndex).mType != EVENT_RELEASE_BUFFER) {
+ // Was not actually presented.
+ ++missedCnt;
+ continue;
+ }
+
+ postTimes.add(bufferEvents.get(i).mDuration);
+ readyTimes.add(
+ bufferEvents.get(acquireIndex).mTime - bufferEvents.get(i).mTime);
+ latencyTimes.add(
+ bufferEvents.get(releaseIndex).mTime - bufferEvents.get(i).mTime);
+ }
+
+ if (postTimes.size() < MINIMAL_SAMPLE_CNT_TO_PASS) {
+ throw new IllegalStateException("Too few sample cnt: " + postTimes.size() +". " +
+ MINIMAL_SAMPLE_CNT_TO_PASS + " is required.");
+ }
+
+ Map<String, Double> status = new TreeMap<>();
+ addResults(status, testTag, KEY_POST_TIME, postTimes);
+ addResults(status, testTag, KEY_READY_TIME, readyTimes);
+ addResults(status, testTag, KEY_LATENCY, latencyTimes);
+ status.put(testTag + "_" + KEY_MISSED_FRAME_RATE,
+ 100.0 * missedCnt / (missedCnt + postTimes.size()));
+ return status;
+ }
+
+ private static void addResults(
+ Map<String, Double> status, String tag, String key, List<Long> times) {
+ Collections.sort(times);
+ long min = times.get(0);
+ long max = times.get(0);
+ for (long time : times) {
+ min = Math.min(min, time);
+ max = Math.max(max, time);
+ }
+ status.put(tag + "_" + key + "_" + SUFFIX_MIN, (double)min);
+ status.put(tag + "_" + key + "_" + SUFFIX_MAX, (double)max);
+ status.put(tag + "_" + key + "_" + SUFFIX_MEDIAN, (double)times.get(times.size() / 2));
+ }
+
+ // Helper to convert surface flinger events to buffer events.
+ private static void toBufferEvents(
+ int type, List<RawEvent> rawEvents, List<BufferEvent> bufferEvents) {
+ for (RawEvent event : rawEvents) {
+ if (event.mChildren.isEmpty()) {
+ throw new IllegalStateException("Buffer name is expected");
+ }
+ final String bufferName = event.mChildren.get(0).mName;
+ if (bufferName.startsWith("SurfaceView - android.gameperformance")) {
+ bufferEvents.add(
+ new BufferEvent(type, event.mTime, event.mDuration, bufferName));
+ }
+ }
+ }
+
+ private static int findNextOfType(List<BufferEvent> events, int startIndex, int type) {
+ for (int i = startIndex; i < events.size(); ++i) {
+ if (events.get(i).mType == type) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static int findNextOfBufferId(
+ List<BufferEvent> events, int startIndex, String bufferId) {
+ for (int i = startIndex; i < events.size(); ++i) {
+ if (bufferId.equals(events.get(i).mBufferId)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 1) {
+ System.err.println("Usage: " + TAG + " atrace.log");
+ return;
+ }
+
+ try {
+ System.out.println("Results:");
+ for (Map.Entry<?, ?> entry :
+ processGraphicBufferResult(args[0], "default").entrySet()) {
+ System.out.println(" " + entry.getKey() + " = " + entry.getValue());
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/tests/GamePerformance/src/android/gameperformance/Utils.java b/tests/GamePerformance/src/android/gameperformance/Utils.java
new file mode 100644
index 0000000..6481971
--- /dev/null
+++ b/tests/GamePerformance/src/android/gameperformance/Utils.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.gameperformance;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+public class Utils {
+ public static void closeQuietly(Closeable closeable) {
+ try {
+ if (closeable != null) {
+ closeable.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+}